diff --git a/mifosng-db/migrations/core_db/V198__alter_savings_account_for_start_interest_calculation_date.sql b/mifosng-db/migrations/core_db/V198__alter_savings_account_for_start_interest_calculation_date.sql new file mode 100644 index 000000000..06e529d26 --- /dev/null +++ b/mifosng-db/migrations/core_db/V198__alter_savings_account_for_start_interest_calculation_date.sql @@ -0,0 +1,2 @@ +ALTER TABLE `m_savings_account` +ADD COLUMN `start_interest_calculation_date` DATE NULL DEFAULT NULL AFTER `min_balance_for_interest_calculation`; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 31e580084..3ab0e691b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -246,6 +246,10 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true) private BigDecimal minRequiredBalance; + + @Temporal(TemporalType.DATE) + @Column(name = "start_interest_calculation_date") + protected Date startInterestCalculationDate; @Embedded protected SavingsAccountSummary summary; @@ -468,6 +472,21 @@ public class SavingsAccount extends AbstractPersistable { return postingTransation; } + + //Determine the last transaction for given day + protected SavingsAccountTransaction findLastTransaction(final LocalDate date) { + + SavingsAccountTransaction savingsTransaction = null; + + for (final SavingsAccountTransaction transaction : this.transactions) { + if (transaction.isNotReversed() && transaction.occursOn(date)) { + savingsTransaction = transaction; + break; + } + } + + return savingsTransaction; + } /** * All interest calculation based on END-OF-DAY-BALANCE. @@ -510,12 +529,42 @@ public class SavingsAccount extends AbstractPersistable { final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType .fromInt(this.interestCalculationDaysInYearType); - final List postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(getActivationLocalDate(), + final List postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType); final List allPostingPeriods = new ArrayList<>(); + + Money periodStartingBalance; + if (this.startInterestCalculationDate != null) { + LocalDate startInterestCalculationDate = new LocalDate( + this.startInterestCalculationDate); + final SavingsAccountTransaction transaction = findLastTransaction(startInterestCalculationDate); - Money periodStartingBalance = Money.zero(this.currency); + if (transaction == null) { + final String defaultUserMessage = "No transactions were found on the specified date " + + getStartInterestCalculationDate().toString() + + " for account number " + + this.accountNumber.toString() + + " and resource id " + + getId(); + + final ApiParameterError error = ApiParameterError + .parameterError( + "error.msg.savingsaccount.transaction.incorrect.start.interest.calculation.date", + defaultUserMessage, "transactionDate", + getStartInterestCalculationDate().toString()); + + final List dataValidationErrors = new ArrayList<>(); + dataValidationErrors.add(error); + + throw new PlatformApiDataValidationException( + dataValidationErrors); + } + + periodStartingBalance = transaction + .getRunningBalance(this.currency); + } else + periodStartingBalance = Money.zero(this.currency); final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType); final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate); @@ -700,6 +749,18 @@ public class SavingsAccount extends AbstractPersistable { } return activationLocalDate; } + + //startInterestCalculationDate is set during migration so that there is no + //interference with interest posting of previous system + public LocalDate getStartInterestCalculationDate() { + LocalDate startInterestCalculationLocalDate = null; + if (this.startInterestCalculationDate != null) { + startInterestCalculationLocalDate = new LocalDate( + this.startInterestCalculationDate); + } else + startInterestCalculationLocalDate = getActivationLocalDate(); + return startInterestCalculationLocalDate; + } public SavingsAccountTransaction withdraw(final SavingsAccountTransactionDTO transactionDTO, final boolean applyWithdrawFee) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 3ad6a8239..f0523b5f4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -216,6 +216,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable { public Money getAmount(final MonetaryCurrency currency) { return Money.of(currency, this.amount); } + + public Money getRunningBalance(final MonetaryCurrency currency) { + return Money.of(currency, this.runningBalance); + } public boolean isDeposit() { return SavingsAccountTransactionType.fromInt(this.typeOf).isDeposit(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java index e0ff36681..1701477af 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsHelper.java @@ -29,12 +29,12 @@ public final class SavingsHelper { private final CompoundInterestHelper compoundInterestHelper = new CompoundInterestHelper(); - public List determineInterestPostingPeriods(final LocalDate activationLocalDate, + public List determineInterestPostingPeriods(final LocalDate startInterestCalculationLocalDate, final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType) { final List postingPeriods = new ArrayList<>(); - LocalDate periodStartDate = activationLocalDate; + LocalDate periodStartDate = startInterestCalculationLocalDate; LocalDate periodEndDate = periodStartDate; while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {