MIFOSX-1850 : Recurring deposits without maturity date

This commit is contained in:
Pramod Nuthakki
2015-02-24 10:45:10 +05:30
parent 7ba9a81e53
commit 4a2a85d20e
12 changed files with 274 additions and 87 deletions
@@ -23,7 +23,8 @@ public enum JobName {
UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"), UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"),
TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"), TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"),
ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions"), ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions"),
RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"); RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"),
GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule");
private final String name; private final String name;
@@ -0,0 +1,47 @@
/**
* 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;
import org.joda.time.LocalDate;
import org.mifosplatform.portfolio.calendar.domain.CalendarFrequencyType;
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
public class DepositAccountUtils {
public static final int GENERATE_MINIMUM_NUMBER_OF_FUTURE_INSTALMENTS = 5;
public static LocalDate calculateNextDepositDate(final LocalDate lastDepositDate, final PeriodFrequencyType frequency,
final int recurringEvery) {
LocalDate nextDepositDate = lastDepositDate;
switch (frequency) {
case DAYS:
nextDepositDate = lastDepositDate.plusDays(recurringEvery);
break;
case WEEKS:
nextDepositDate = lastDepositDate.plusWeeks(recurringEvery);
break;
case MONTHS:
nextDepositDate = lastDepositDate.plusMonths(recurringEvery);
break;
case YEARS:
nextDepositDate = lastDepositDate.plusYears(recurringEvery);
break;
case INVALID:
break;
}
return nextDepositDate;
}
public static LocalDate calculateNextDepositDate(final LocalDate lastDepositDate, final String recurrence) {
final PeriodFrequencyType frequencyType = CalendarFrequencyType.from(CalendarUtils.getFrequency(recurrence));
Integer frequency = CalendarUtils.getInterval(recurrence);
frequency = frequency == -1 ? 1 : frequency;
return calculateNextDepositDate(lastDepositDate, frequencyType, frequency);
}
}
@@ -125,7 +125,7 @@ public class DepositAccountDataValidator {
validateDepositDetailsForUpdate(element, baseDataValidator); validateDepositDetailsForUpdate(element, baseDataValidator);
validatePreClosureDetailForUpdate(element, baseDataValidator); validatePreClosureDetailForUpdate(element, baseDataValidator);
validateDepositTermDeatilForUpdate(element, baseDataValidator); validateDepositTermDeatilForUpdate(element, baseDataValidator, DepositAccountType.FIXED_DEPOSIT);
// validateSavingsCharges(element, baseDataValidator); // validateSavingsCharges(element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
@@ -166,7 +166,7 @@ public class DepositAccountDataValidator {
validateDepositDetailsForUpdate(element, baseDataValidator); validateDepositDetailsForUpdate(element, baseDataValidator);
validatePreClosureDetailForUpdate(element, baseDataValidator); validatePreClosureDetailForUpdate(element, baseDataValidator);
validateDepositTermDeatilForUpdate(element, baseDataValidator); validateDepositTermDeatilForUpdate(element, baseDataValidator, DepositAccountType.RECURRING_DEPOSIT);
validateRecurringDetailForUpdate(element, baseDataValidator); validateRecurringDetailForUpdate(element, baseDataValidator);
// validateSavingsCharges(element, baseDataValidator); // validateSavingsCharges(element, baseDataValidator);
@@ -494,8 +494,10 @@ public class DepositAccountDataValidator {
baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notNull().positiveAmount(); baseDataValidator.reset().parameter(depositAmountParamName).value(depositAmount).notNull().positiveAmount();
} }
final Integer depositPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodParamName, element); if (depositType.isFixedDeposit() || fromApiJsonHelper.parameterExists(depositPeriodParamName, element)) {
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull().integerGreaterThanZero(); final Integer depositPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodParamName, element);
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull().integerGreaterThanZero();
}
final Integer depositPeriodFrequencyId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodFrequencyIdParamName, final Integer depositPeriodFrequencyId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodFrequencyIdParamName,
element); element);
@@ -503,7 +505,8 @@ public class DepositAccountDataValidator {
.isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues()); .isOneOfTheseValues(SavingsPeriodFrequencyType.integerValues());
} }
private void validateDepositTermDeatilForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator) { private void validateDepositTermDeatilForUpdate(final JsonElement element, final DataValidatorBuilder baseDataValidator,
final DepositAccountType depositAccountType) {
this.productDataValidator.validateDepositTermDetailForUpdate(element, baseDataValidator); this.productDataValidator.validateDepositTermDetailForUpdate(element, baseDataValidator);
if (fromApiJsonHelper.parameterExists(depositAmountParamName, element)) { if (fromApiJsonHelper.parameterExists(depositAmountParamName, element)) {
@@ -513,7 +516,11 @@ public class DepositAccountDataValidator {
if (fromApiJsonHelper.parameterExists(depositPeriodParamName, element)) { if (fromApiJsonHelper.parameterExists(depositPeriodParamName, element)) {
final Integer depositPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodParamName, element); final Integer depositPeriod = fromApiJsonHelper.extractIntegerSansLocaleNamed(depositPeriodParamName, element);
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull().integerGreaterThanZero(); if (depositAccountType.isFixedDeposit()) {
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull().integerGreaterThanZero();
} else {
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull().integerGreaterThanZero();
}
} }
if (fromApiJsonHelper.parameterExists(depositPeriodFrequencyIdParamName, element)) { if (fromApiJsonHelper.parameterExists(depositPeriodFrequencyIdParamName, element)) {
@@ -221,7 +221,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
Long savingsTransactionId = null; Long savingsTransactionId = null;
account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, closedDate);
final BigDecimal transactionAmount = account.getAccountBalance(); final BigDecimal transactionAmount = account.getAccountBalance();
final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName);
final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId);
@@ -195,6 +195,11 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistable<Long> {
this.maturityAmount = this.depositAmount.add(interestPayable); this.maturityAmount = this.depositAmount.add(interestPayable);
this.maturityDate = maturityDate.toDate(); this.maturityDate = maturityDate.toDate();
} }
public void updateDepositAmount(final BigDecimal depositAmount) {
this.depositAmount = depositAmount;
}
public LocalDate getMaturityLocalDate() { public LocalDate getMaturityLocalDate() {
LocalDate maturityLocalDate = null; LocalDate maturityLocalDate = null;
@@ -49,6 +49,7 @@ import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart; import org.mifosplatform.portfolio.interestratechart.domain.InterestRateChart;
import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType; import org.mifosplatform.portfolio.savings.DepositAccountOnClosureType;
import org.mifosplatform.portfolio.savings.DepositAccountUtils;
import org.mifosplatform.portfolio.savings.DepositsApiConstants; import org.mifosplatform.portfolio.savings.DepositsApiConstants;
import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType; import org.mifosplatform.portfolio.savings.PreClosurePenalInterestOnType;
import org.mifosplatform.portfolio.savings.SavingsApiConstants; import org.mifosplatform.portfolio.savings.SavingsApiConstants;
@@ -192,6 +193,10 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
} }
if (depositCloseDate == null) {
depositCloseDate = LocalDate.now();
}
final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount(); final BigDecimal depositAmount = accountTermAndPreClosure.depositAmount();
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate, BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), depositCloseDate,
this.client); this.client);
@@ -209,8 +214,16 @@ public class RecurringDepositAccount extends SavingsAccount {
public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure, public void updateMaturityDateAndAmount(final MathContext mc, final boolean isPreMatureClosure,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
final LocalDate maturityDate = calculateMaturityDate(); final LocalDate maturityDate = calculateMaturityDate();
final LocalDate interestCalculationUpto = maturityDate.minusDays(1); LocalDate interestCalculationUpto = null;
final List<SavingsAccountTransaction> allTransactions = generateIncludingFutureTransactions(interestCalculationUpto); List<SavingsAccountTransaction> allTransactions = null;
if (maturityDate == null) {
interestCalculationUpto = DateUtils.getLocalDateOfTenant();
allTransactions = getTransactions(interestCalculationUpto, false);
} else {
interestCalculationUpto = maturityDate.minusDays(1);
allTransactions = getTransactions(interestCalculationUpto, true);
}
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, interestCalculationUpto, allTransactions, final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, interestCalculationUpto, allTransactions,
isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
@@ -220,8 +233,12 @@ public class RecurringDepositAccount extends SavingsAccount {
totalInterestPayable = totalInterestPayable.plus(postingPeriod.getInterestEarned()); totalInterestPayable = totalInterestPayable.plus(postingPeriod.getInterestEarned());
totalDepositAmount = totalDepositAmount.plus(postingPeriod.closingBalance()).minus(postingPeriod.openingBalance()); totalDepositAmount = totalDepositAmount.plus(postingPeriod.closingBalance()).minus(postingPeriod.openingBalance());
} }
if (maturityDate == null) {
this.accountTermAndPreClosure.updateMaturityDetails(totalDepositAmount.getAmount(), totalInterestPayable.getAmount(), maturityDate); this.accountTermAndPreClosure.updateDepositAmount(totalDepositAmount.getAmount());
} else {
this.accountTermAndPreClosure.updateMaturityDetails(totalDepositAmount.getAmount(), totalInterestPayable.getAmount(),
maturityDate);
}
} }
public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) {
@@ -239,7 +256,7 @@ public class RecurringDepositAccount extends SavingsAccount {
if (!this.maturityDate().isAfter(todayDate)) { if (!this.maturityDate().isAfter(todayDate)) {
// update account status // update account status
this.status = SavingsAccountStatusType.MATURED.getValue(); this.status = SavingsAccountStatusType.MATURED.getValue();
postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, todayDate);
} }
} }
@@ -248,6 +265,7 @@ public class RecurringDepositAccount extends SavingsAccount {
final LocalDate startDate = depositStartDate(); final LocalDate startDate = depositStartDate();
LocalDate maturityDate = null; LocalDate maturityDate = null;
final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod(); final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod();
if (depositPeriod == null) { return maturityDate; }
switch (this.accountTermAndPreClosure.depositPeriodFrequencyType()) { switch (this.accountTermAndPreClosure.depositPeriodFrequencyType()) {
case DAYS: case DAYS:
maturityDate = startDate.plusDays(depositPeriod); maturityDate = startDate.plusDays(depositPeriod);
@@ -315,16 +333,18 @@ public class RecurringDepositAccount extends SavingsAccount {
return allPostingPeriods; return allPostingPeriods;
} }
private List<SavingsAccountTransaction> generateIncludingFutureTransactions(final LocalDate depositEndDate) { private List<SavingsAccountTransaction> getTransactions(final LocalDate depositEndDate, final boolean generateFutureTransactions) {
List<SavingsAccountTransaction> allTransactions = new ArrayList<>(); List<SavingsAccountTransaction> allTransactions = new ArrayList<>();
// add existing transactions // add existing transactions
allTransactions.addAll(retreiveOrderedNonInterestPostingTransactions()); allTransactions.addAll(retreiveOrderedNonInterestPostingTransactions());
for (RecurringDepositScheduleInstallment installment : depositScheduleInstallments()) { if (generateFutureTransactions) {
if (installment.isPrincipalNotCompleted(getCurrency())) { for (RecurringDepositScheduleInstallment installment : depositScheduleInstallments()) {
final SavingsAccountTransaction transaction = SavingsAccountTransaction if (installment.isPrincipalNotCompleted(getCurrency())) {
.deposit(null, office(), null, installment.dueDate(), installment.getDepositAmountOutstanding(getCurrency()), final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null,
installment.dueDate().toDate(), null); installment.dueDate(), installment.getDepositAmountOutstanding(getCurrency()), installment.dueDate().toDate(),
allTransactions.add(transaction); null);
allTransactions.add(transaction);
}
} }
} }
@@ -358,29 +378,6 @@ public class RecurringDepositAccount extends SavingsAccount {
return allTransactions; return allTransactions;
} }
private LocalDate calculateNextDepositDate(final LocalDate lastDepositDate, final PeriodFrequencyType frequency,
final int recurringEvery) {
LocalDate nextDepositDate = lastDepositDate;
switch (frequency) {
case DAYS:
nextDepositDate = lastDepositDate.plusDays(recurringEvery);
break;
case WEEKS:
nextDepositDate = lastDepositDate.plusWeeks(recurringEvery);
break;
case MONTHS:
nextDepositDate = lastDepositDate.plusMonths(recurringEvery);
break;
case YEARS:
nextDepositDate = lastDepositDate.plusYears(recurringEvery);
break;
case INVALID:
break;
}
return nextDepositDate;
}
public LocalDate depositStartDate() { public LocalDate depositStartDate() {
final LocalDate depositStartDate = accountTermAndPreClosure.getExpectedFirstDepositOnDate(); final LocalDate depositStartDate = accountTermAndPreClosure.getExpectedFirstDepositOnDate();
if (depositStartDate == null) return accountSubmittedOrActivationDate(); if (depositStartDate == null) return accountSubmittedOrActivationDate();
@@ -500,7 +497,7 @@ public class RecurringDepositAccount extends SavingsAccount {
.resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction); .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction);
final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status);
if (!SavingsAccountStatusType.MATURED.hasStateOf(currentStatus)) { if (!SavingsAccountStatusType.MATURED.hasStateOf(currentStatus) && this.maturityDate() != null) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.matured.state"); baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.matured.state");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
} }
@@ -514,7 +511,7 @@ public class RecurringDepositAccount extends SavingsAccount {
.failWithCode("must.be.after.activation.date"); .failWithCode("must.be.after.activation.date");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
} }
if (closedDate.isBefore(maturityDate())) { if (maturityDate() != null && closedDate.isBefore(maturityDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.account.maturity.date"); .failWithCode("must.be.after.account.maturity.date");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -564,11 +561,15 @@ public class RecurringDepositAccount extends SavingsAccount {
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
} }
public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
final LocalDate interestPostingUpToDate = maturityDate(); final LocalDate closeDate) {
LocalDate interestPostingUpToDate = maturityDate();
if (interestPostingUpToDate == null) {
interestPostingUpToDate = closeDate;
}
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer, final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate.minusDays(1), isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
Money interestPostedToDate = Money.zero(this.currency); Money interestPostedToDate = Money.zero(this.currency);
@@ -895,7 +896,7 @@ public class RecurringDepositAccount extends SavingsAccount {
private boolean isBeforeMaturityDate(final LocalDate compareDate) { private boolean isBeforeMaturityDate(final LocalDate compareDate) {
final LocalDate maturityDate = this.maturityDate(); final LocalDate maturityDate = this.maturityDate();
return compareDate.isBefore(maturityDate); return maturityDate == null ? true : compareDate.isBefore(maturityDate);
} }
private boolean isBeforeDepositStartDate(LocalDate compareDate) { private boolean isBeforeDepositStartDate(LocalDate compareDate) {
@@ -923,30 +924,35 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod(); final Integer depositPeriod = this.accountTermAndPreClosure.depositPeriod();
final SavingsPeriodFrequencyType depositPeriodFrequencyType = this.accountTermAndPreClosure.depositPeriodFrequencyType(); if (this.accountTermAndPreClosure.depositTermDetail().maxDepositTerm() != null) {
final boolean isValidDepositPeriod = this.accountTermAndPreClosure.depositTermDetail().isDepositBetweenMinAndMax( baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod).notNull();
depositStartDate(), calculateMaturityDate()); }
if (!isValidDepositPeriod) { if (depositPeriod != null) {
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod) final SavingsPeriodFrequencyType depositPeriodFrequencyType = this.accountTermAndPreClosure.depositPeriodFrequencyType();
.failWithCodeNoParameterAddedToErrorCode("deposit.period.not.between.min.and.max.deposit.term"); final boolean isValidDepositPeriod = this.accountTermAndPreClosure.depositTermDetail().isDepositBetweenMinAndMax(
} else { depositStartDate(), calculateMaturityDate());
final Integer inMultiplesOf = this.accountTermAndPreClosure.depositTermDetail().inMultiplesOfDepositTerm(); if (!isValidDepositPeriod) {
if (inMultiplesOf != null) { baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod)
final boolean isValid = this.accountTermAndPreClosure.depositTermDetail().isValidInMultiplesOfPeriod(depositPeriod, .failWithCodeNoParameterAddedToErrorCode("deposit.period.not.between.min.and.max.deposit.term");
depositPeriodFrequencyType); } else {
if (!isValid) { final Integer inMultiplesOf = this.accountTermAndPreClosure.depositTermDetail().inMultiplesOfDepositTerm();
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod) if (inMultiplesOf != null) {
.failWithCodeNoParameterAddedToErrorCode("deposit.period.not.multiple.of.term"); final boolean isValid = this.accountTermAndPreClosure.depositTermDetail().isValidInMultiplesOfPeriod(depositPeriod,
depositPeriodFrequencyType);
if (!isValid) {
baseDataValidator.reset().parameter(depositPeriodParamName).value(depositPeriod)
.failWithCodeNoParameterAddedToErrorCode("deposit.period.not.multiple.of.term");
}
} }
} }
} if (isAccountLocked(calculateMaturityDate())) {
if (isAccountLocked(calculateMaturityDate())) { baseDataValidator
baseDataValidator .reset()
.reset() .parameter(depositPeriodParamName)
.parameter(depositPeriodParamName) .value(depositPeriod)
.value(depositPeriod) .failWithCode("deposit.period.must.be.greater.than.lock.in.period",
.failWithCode("deposit.period.must.be.greater.than.lock.in.period", "Deposit period must be greater than account lock-in period.");
"Deposit period must be greater than account lock-in period."); }
} }
if (firstDepositDateBeforeAccountSubmittedOrActivationDate()) { if (firstDepositDateBeforeAccountSubmittedOrActivationDate()) {
@@ -969,8 +975,8 @@ public class RecurringDepositAccount extends SavingsAccount {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
LocalDate maturityDate = calculateMaturityDate();
if (this.chart != null) { if (this.chart != null && maturityDate != null) {
final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate(); final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate();
LocalDate chartEndDate = this.chart.getEndDateAsLocalDate(); LocalDate chartEndDate = this.chart.getEndDateAsLocalDate();
chartEndDate = chartEndDate == null ? DateUtils.getLocalDateOfTenant() : chartEndDate; chartEndDate = chartEndDate == null ? DateUtils.getLocalDateOfTenant() : chartEndDate;
@@ -981,8 +987,8 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
final BigDecimal maturityAmount = this.accountTermAndPreClosure.maturityAmount(); final BigDecimal maturityAmount = this.accountTermAndPreClosure.maturityAmount();
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(maturityAmount, depositStartDate(), BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(maturityAmount, depositStartDate(), maturityDate,
calculateMaturityDate(), this.client); this.client);
if (applicableInterestRate.equals(BigDecimal.ZERO)) { if (applicableInterestRate.equals(BigDecimal.ZERO)) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
@@ -1088,18 +1094,28 @@ public class RecurringDepositAccount extends SavingsAccount {
installmentDate = depositStartDate(); installmentDate = depositStartDate();
} }
final LocalDate maturityDate = calculateMaturityDate();
int installmentNumber = 1; int installmentNumber = 1;
final LocalDate maturityDate = calcualteScheduleTillDate(frequency, recurringEvery);
final BigDecimal depositAmount = this.recurringDetail.mandatoryRecommendedDepositAmount(); final BigDecimal depositAmount = this.recurringDetail.mandatoryRecommendedDepositAmount();
while (maturityDate.isAfter(installmentDate)) { while (maturityDate.isAfter(installmentDate)) {
final RecurringDepositScheduleInstallment installment = RecurringDepositScheduleInstallment.installment(this, final RecurringDepositScheduleInstallment installment = RecurringDepositScheduleInstallment.installment(this,
installmentNumber, installmentDate.toDate(), depositAmount); installmentNumber, installmentDate.toDate(), depositAmount);
depositScheduleInstallments.add(installment); depositScheduleInstallments.add(installment);
installmentDate = calculateNextDepositDate(installmentDate, frequency, recurringEvery); installmentDate = DepositAccountUtils.calculateNextDepositDate(installmentDate, frequency, recurringEvery);
installmentNumber += 1; installmentNumber += 1;
} }
} }
private LocalDate calcualteScheduleTillDate(final PeriodFrequencyType frequency, final Integer recurringEvery) {
LocalDate tillDate = calculateMaturityDate();
if (tillDate == null) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
tillDate = DepositAccountUtils.calculateNextDepositDate(today, frequency, recurringEvery
* (DepositAccountUtils.GENERATE_MINIMUM_NUMBER_OF_FUTURE_INSTALMENTS + 1));
}
return tillDate;
}
private List<RecurringDepositScheduleInstallment> depositScheduleInstallments() { private List<RecurringDepositScheduleInstallment> depositScheduleInstallments() {
if (this.depositScheduleInstallments == null) { if (this.depositScheduleInstallments == null) {
this.depositScheduleInstallments = new ArrayList<>(); this.depositScheduleInstallments = new ArrayList<>();
@@ -1113,7 +1129,7 @@ public class RecurringDepositAccount extends SavingsAccount {
public void updateOverduePayments(final LocalDate todayDate) { public void updateOverduePayments(final LocalDate todayDate) {
LocalDate overdueUptoDate = this.maturityDate(); LocalDate overdueUptoDate = this.maturityDate();
if (overdueUptoDate.isAfter(todayDate)) { if (overdueUptoDate == null || overdueUptoDate.isAfter(todayDate)) {
overdueUptoDate = todayDate; overdueUptoDate = todayDate;
} }
@@ -6,6 +6,7 @@
package org.mifosplatform.portfolio.savings.service; package org.mifosplatform.portfolio.savings.service;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import org.mifosplatform.infrastructure.core.data.PaginationParameters; import org.mifosplatform.infrastructure.core.data.PaginationParameters;
import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.Page;
@@ -38,4 +39,6 @@ public interface DepositAccountReadPlatformService {
SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(final Long accountId); SavingsAccountTransactionData retrieveRecurringAccountDepositTransactionTemplate(final Long accountId);
Collection<AccountTransferDTO> retrieveDataForInterestTransfer(); Collection<AccountTransferDTO> retrieveDataForInterestTransfer();
Collection<Map<String, Object>> retriveDataForRDScheduleCreation();
} }
@@ -10,14 +10,18 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.data.PaginationParameters; import org.mifosplatform.infrastructure.core.data.PaginationParameters;
import org.mifosplatform.infrastructure.core.data.PaginationParametersDataValidator; import org.mifosplatform.infrastructure.core.data.PaginationParametersDataValidator;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.core.service.PaginationHelper; import org.mifosplatform.infrastructure.core.service.PaginationHelper;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
@@ -32,6 +36,7 @@ import org.mifosplatform.portfolio.account.domain.AccountTransferType;
import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.calendar.data.CalendarData;
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
import org.mifosplatform.portfolio.calendar.domain.CalendarFrequencyType; import org.mifosplatform.portfolio.calendar.domain.CalendarFrequencyType;
import org.mifosplatform.portfolio.calendar.domain.CalendarType;
import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService; import org.mifosplatform.portfolio.calendar.service.CalendarReadPlatformService;
import org.mifosplatform.portfolio.charge.data.ChargeData; import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService; import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
@@ -103,6 +108,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
private final RecurringAccountDepositTransactionTemplateMapper rdTransactionTemplateMapper; private final RecurringAccountDepositTransactionTemplateMapper rdTransactionTemplateMapper;
private final DropdownReadPlatformService dropdownReadPlatformService; private final DropdownReadPlatformService dropdownReadPlatformService;
private final CalendarReadPlatformService calendarReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService;
private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
@Autowired @Autowired
public DepositAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, public DepositAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
@@ -197,9 +203,13 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
sqlBuilder.append(this.depositAccountForMaturityRowMapper.schema()); sqlBuilder.append(this.depositAccountForMaturityRowMapper.schema());
sqlBuilder.append(" WHERE da.deposit_type_enum in (?, ?) and da.status_enum = ?"); sqlBuilder.append(" WHERE da.deposit_type_enum in (?, ?) and da.status_enum = ?");
return this.jdbcTemplate.query(sqlBuilder.toString(), this.depositAccountForMaturityRowMapper, new Object[] { LocalDate today = DateUtils.getLocalDateOfTenant();
DepositAccountType.FIXED_DEPOSIT.getValue(), DepositAccountType.RECURRING_DEPOSIT.getValue(),
SavingsAccountStatusType.ACTIVE.getValue() }); return this.jdbcTemplate.query(
sqlBuilder.toString(),
this.depositAccountForMaturityRowMapper,
new Object[] { formatter.print(today), DepositAccountType.FIXED_DEPOSIT.getValue(),
DepositAccountType.RECURRING_DEPOSIT.getValue(), SavingsAccountStatusType.ACTIVE.getValue() });
} }
@Override @Override
@@ -482,6 +492,27 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
SavingsAccountTransactionType.INTEREST_POSTING.getValue(), SavingsAccountStatusType.ACTIVE.getValue() }); SavingsAccountTransactionType.INTEREST_POSTING.getValue(), SavingsAccountStatusType.ACTIVE.getValue() });
} }
@Override
public Collection<Map<String, Object>> retriveDataForRDScheduleCreation() {
final StringBuilder sb = new StringBuilder(300);
sb.append(" select rd.savings_account_id savingsId, rd.mandatory_recommended_deposit_amount as amount,");
sb.append(" mc.recurrence as recurrence ,");
sb.append(" max(ms.duedate) as dueDate , max(ms.installment) as installment,");
sb.append(" count(ms.installment) as futureInstallemts");
sb.append(" from m_deposit_account_term_and_preclosure dat ");
sb.append(" inner join m_savings_account sa on sa.id = dat.savings_account_id and sa.status_enum = ?");
sb.append(" inner join m_deposit_account_recurring_detail rd on rd.savings_account_id = dat.savings_account_id ");
sb.append(" inner join m_calendar_instance mci on mci.entity_type_enum = ? and mci.entity_id = dat.savings_account_id ");
sb.append(" inner join m_calendar mc on mc.id = mci.calendar_id and mc.calendar_type_enum = ?");
sb.append(" inner join m_mandatory_savings_schedule ms on ms.savings_account_id = dat.savings_account_id and ms.duedate > ?");
sb.append(" where dat.deposit_period is null");
sb.append(" group by ms.savings_account_id");
return this.jdbcTemplate.queryForList(sb.toString(), SavingsAccountStatusType.ACTIVE.getValue(),
CalendarEntityType.SAVINGS.getValue(), CalendarType.COLLECTION.getValue(),
formatter.print(DateUtils.getLocalDateOfTenant()));
}
private static abstract class DepositAccountMapper implements RowMapper<DepositAccountData> { private static abstract class DepositAccountMapper implements RowMapper<DepositAccountData> {
private final String selectFieldsSql; private final String selectFieldsSql;
@@ -1300,6 +1331,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
sqlBuilder.append("da.account_no as accountNumber, "); sqlBuilder.append("da.account_no as accountNumber, ");
sqlBuilder.append("da.deposit_type_enum as depositTypeId "); sqlBuilder.append("da.deposit_type_enum as depositTypeId ");
sqlBuilder.append("FROM m_savings_account da "); sqlBuilder.append("FROM m_savings_account da ");
sqlBuilder.append("inner join m_deposit_account_term_and_preclosure dat on dat.savings_account_id = da.id ");
sqlBuilder.append("and dat.maturity_date is not null and dat.maturity_date <= ? ");
this.schemaSql = sqlBuilder.toString(); this.schemaSql = sqlBuilder.toString();
} }
@@ -5,6 +5,7 @@
*/ */
package org.mifosplatform.portfolio.savings.service; package org.mifosplatform.portfolio.savings.service;
import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
@@ -312,8 +313,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
financialYearBeginningMonth); financialYearBeginningMonth);
} }
final LocalDate overdueUptoDate = account.maturityDate().isAfter(DateUtils.getLocalDateOfTenant()) ? DateUtils final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
.getLocalDateOfTenant() : account.maturityDate();
account.updateOverduePayments(overdueUptoDate); account.updateOverduePayments(overdueUptoDate);
final boolean isInterestTransfer = false; final boolean isInterestTransfer = false;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) { if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) {
@@ -586,8 +586,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final boolean isPreMatureClosure = false; final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
final LocalDate overdueUptoDate = account.maturityDate().isAfter(DateUtils.getLocalDateOfTenant()) ? DateUtils final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
.getLocalDateOfTenant() : account.maturityDate();
if (savingsAccountTransaction.isDeposit()) { if (savingsAccountTransaction.isDeposit()) {
account.updateScheduleInstallments(); account.updateScheduleInstallments();
@@ -682,8 +681,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
if (savingsAccountTransaction.isDeposit()) { if (savingsAccountTransaction.isDeposit()) {
account.handleScheduleInstallments(savingsAccountTransaction); account.handleScheduleInstallments(savingsAccountTransaction);
} }
final LocalDate overdueUptoDate = account.maturityDate().isAfter(DateUtils.getLocalDateOfTenant()) ? DateUtils final LocalDate overdueUptoDate = DateUtils.getLocalDateOfTenant();
.getLocalDateOfTenant() : account.maturityDate();
account.updateOverduePayments(overdueUptoDate); account.updateOverduePayments(overdueUptoDate);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -833,6 +831,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId, final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId,
DepositAccountType.RECURRING_DEPOSIT); DepositAccountType.RECURRING_DEPOSIT);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
if (account.maturityDate() == null) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.preMatureCloseAction);
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("can.not.close.as.premature");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
this.depositAccountDomainService.handleRDAccountPreMatureClosure(account, paymentDetail, user, command, this.depositAccountDomainService.handleRDAccountPreMatureClosure(account, paymentDetail, user, command,
DateUtils.getLocalDateOfTenant(), changes); DateUtils.getLocalDateOfTenant(), changes);
@@ -20,4 +20,6 @@ public interface ScheduledJobRunnerService {
void updateNPA(); void updateNPA();
void updateMaturityDetailsOfDepositAccounts(); void updateMaturityDetailsOfDepositAccounts();
void generateRDSchedule();
} }
@@ -5,17 +5,25 @@
*/ */
package org.mifosplatform.scheduledjobs.service; package org.mifosplatform.scheduledjobs.service;
import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.core.service.RoutingDataSourceServiceFactory; import org.mifosplatform.infrastructure.core.service.RoutingDataSourceServiceFactory;
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException; import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
import org.mifosplatform.infrastructure.jobs.service.JobName; import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.portfolio.savings.DepositAccountType; import org.mifosplatform.portfolio.savings.DepositAccountType;
import org.mifosplatform.portfolio.savings.DepositAccountUtils;
import org.mifosplatform.portfolio.savings.data.DepositAccountData; import org.mifosplatform.portfolio.savings.data.DepositAccountData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData;
import org.mifosplatform.portfolio.savings.service.DepositAccountReadPlatformService; import org.mifosplatform.portfolio.savings.service.DepositAccountReadPlatformService;
@@ -33,6 +41,8 @@ import org.springframework.transaction.annotation.Transactional;
public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService { public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService {
private final static Logger logger = LoggerFactory.getLogger(ScheduledJobRunnerServiceImpl.class); private final static Logger logger = LoggerFactory.getLogger(ScheduledJobRunnerServiceImpl.class);
private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
private final DateTimeFormatter formatterWithTime = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
private final RoutingDataSourceServiceFactory dataSourceServiceFactory; private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
@@ -285,4 +295,60 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Deposit accounts affected by update: " + depositAccounts.size()); logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Deposit accounts affected by update: " + depositAccounts.size());
} }
@Override
@CronTarget(jobName = JobName.GENERATE_RD_SCEHDULE)
public void generateRDSchedule() {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSourceServiceFactory.determineDataSourceService().retrieveDataSource());
final Collection<Map<String, Object>> scheduleDetails = this.depositAccountReadPlatformService.retriveDataForRDScheduleCreation();
String insertSql = "INSERT INTO `m_mandatory_savings_schedule` (`savings_account_id`, `duedate`, `installment`, `deposit_amount`, `completed_derived`, `created_date`, `lastmodified_date`) VALUES ";
StringBuilder sb = new StringBuilder();
String currentDate = formatterWithTime.print(DateUtils.getLocalDateTimeOfTenant());
int iterations = 0;
for (Map<String, Object> details : scheduleDetails) {
Long count = (Long) details.get("futureInstallemts");
if (count == null) {
count = 0l;
}
final Long savingsId = (Long) details.get("savingsId");
final BigDecimal amount = (BigDecimal) details.get("amount");
final String recurrence = (String) details.get("recurrence");
Date date = (Date) details.get("dueDate");
LocalDate lastDepositDate = new LocalDate(date);
Integer installmentNumber = (Integer) details.get("installment");
while (count < DepositAccountUtils.GENERATE_MINIMUM_NUMBER_OF_FUTURE_INSTALMENTS) {
count++;
installmentNumber++;
lastDepositDate = DepositAccountUtils.calculateNextDepositDate(lastDepositDate, recurrence);
if (sb.length() > 0) {
sb.append(", ");
}
sb.append("(");
sb.append(savingsId);
sb.append(",'");
sb.append(formatter.print(lastDepositDate));
sb.append("',");
sb.append(installmentNumber);
sb.append(",");
sb.append(amount);
sb.append(", b'0','");
sb.append(currentDate);
sb.append("','");
sb.append(currentDate);
sb.append("')");
iterations++;
if (iterations > 200) {
jdbcTemplate.update(insertSql + sb.toString());
sb = new StringBuilder();
}
}
}
if (sb.length() > 0) {
jdbcTemplate.update(insertSql + sb.toString());
}
}
} }
@@ -0,0 +1,2 @@
INSERT INTO `job` (`name`, `display_name`, `cron_expression`, `create_time`) VALUES ('Generate Mandatory Savings Schedule', 'Generate Mandatory Savings Schedule', '0 5 0 1/1 * ? *', now());