MIFOSX-250: interest posting/crediting added.
This commit is contained in:
+37
-2
@@ -9629,7 +9629,7 @@ Request Body:
|
|||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
<h2>Calculate Interest on Savings Account</h2>
|
<h2>Calculate Interest on Savings Account</h2>
|
||||||
<p>
|
<p>
|
||||||
Calculates interest earned on a savings account based on todays date. It does not attempt to <b>post or credit</b> the interest on the account. That is responsibility of another API that will be called by overnight process.
|
Calculates interest earned on a savings account based on todays date. It does not attempt to <b>post or credit</b> the interest on the account. That is responsibility of the <a href="#savingsaccounts_post_interest" >Post Interest API</a> that will likely be called by overnight process.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="method-example">
|
<div class="method-example">
|
||||||
@@ -9651,10 +9651,37 @@ Request Body:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a id="savingsaccounts_post_interest" name="savingsaccounts_post_interest" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h2>Post Interest on Savings Account</h2>
|
||||||
|
<p>
|
||||||
|
Calculates and Posts interest earned on a savings account based on todays date and whether an interest posting or crediting event is due.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">POST https://Domain Name/api/v1/savingsaccounts/{savingsId}?command=postInterest</code>
|
||||||
|
<code class="method-request">POST savingsaccount/1?command=postInterest
|
||||||
|
Content-Type: application/json
|
||||||
|
Request Body:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"officeId": 1,
|
||||||
|
"clientId": 1,
|
||||||
|
"savingsId": 1,
|
||||||
|
"resourceId": 1
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a id="savingsaccounts_list" name="savingsaccounts_list" class="old-syle-anchor"> </a>
|
<a id="savingsaccounts_list" name="savingsaccounts_list" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
<h4>List Savings Accounts</h4>
|
<h2>List Savings Accounts</h2>
|
||||||
<p>Example Requests:</p>
|
<p>Example Requests:</p>
|
||||||
<div class=apiClick>savingsaccounts</div>
|
<div class=apiClick>savingsaccounts</div>
|
||||||
<br><br>
|
<br><br>
|
||||||
@@ -13498,6 +13525,14 @@ GET https://DomainName/api/v1/search?query=000000111
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>savingsaccounts/{accountId}?command=postInterest</td>
|
||||||
|
<td><a href="#savingsaccounts_post_interest">Post Interest on a Savings Account</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
<tr class="alt">
|
<tr class="alt">
|
||||||
<td><a href="#savingsaccounts_transactions">Savings Transactions</a></td>
|
<td><a href="#savingsaccounts_transactions">Savings Transactions</a></td>
|
||||||
<td>savingsaccounts/{accountId}/transactions?command=deposit</td>
|
<td>savingsaccounts/{accountId}/transactions?command=deposit</td>
|
||||||
|
|||||||
+16
-16
@@ -14,22 +14,22 @@ public class LoanTransactionComparator implements Comparator<LoanTransaction> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(final LoanTransaction o1, final LoanTransaction o2) {
|
public int compare(final LoanTransaction o1, final LoanTransaction o2) {
|
||||||
int compareResult = 0;
|
int compareResult = 0;
|
||||||
final int comparsion = o1.getTransactionDate().compareTo(o2.getTransactionDate());
|
final int comparsion = o1.getTransactionDate().compareTo(o2.getTransactionDate());
|
||||||
if (comparsion == 0) {
|
if (comparsion == 0) {
|
||||||
// equal transaction dates
|
// equal transaction dates
|
||||||
if (o1.isWaiver() && o2.isNotWaiver()) {
|
if (o1.isWaiver() && o2.isNotWaiver()) {
|
||||||
compareResult = -1;
|
compareResult = -1;
|
||||||
} else if (o1.isNotWaiver() && o2.isWaiver()) {
|
} else if (o1.isNotWaiver() && o2.isWaiver()) {
|
||||||
compareResult = 1;
|
compareResult = 1;
|
||||||
} else {
|
} else {
|
||||||
compareResult = 0;
|
compareResult = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
compareResult = comparsion;
|
compareResult = comparsion;
|
||||||
}
|
}
|
||||||
|
|
||||||
return compareResult;
|
return compareResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -3,12 +3,12 @@ package org.mifosplatform.portfolio.savings.domain;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||||
|
|
||||||
public class InterestCompoundingPeriodSummary {
|
public class InterestCompoundingPeriodSummary {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private final LocalDateInterval periodInterval;
|
private final LocalDateInterval periodInterval;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private final BigDecimal openingBalance;
|
private final BigDecimal openingBalance;
|
||||||
@@ -50,4 +50,8 @@ public class InterestCompoundingPeriodSummary {
|
|||||||
public BigDecimal interestUnrounded() {
|
public BigDecimal interestUnrounded() {
|
||||||
return this.interestEarnedUnrounded;
|
return this.interestEarnedUnrounded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean fallsBefore(final LocalDate dateToCheck) {
|
||||||
|
return this.periodInterval.fallsBefore(dateToCheck);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+4
@@ -40,4 +40,8 @@ public class LocalDateInterval {
|
|||||||
private boolean isBetweenInclusive(final LocalDate start, final LocalDate end, final LocalDate target) {
|
private boolean isBetweenInclusive(final LocalDate start, final LocalDate end, final LocalDate target) {
|
||||||
return !target.isBefore(start) && !target.isAfter(end);
|
return !target.isBefore(start) && !target.isAfter(end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean fallsBefore(final LocalDate dateToCheck) {
|
||||||
|
return this.endDate.isBefore(dateToCheck);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+98
-12
@@ -10,6 +10,7 @@ import java.math.MathContext;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -46,6 +47,8 @@ import org.mifosplatform.portfolio.group.domain.Group;
|
|||||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||||
import org.mifosplatform.portfolio.savings.api.SavingsApiConstants;
|
import org.mifosplatform.portfolio.savings.api.SavingsApiConstants;
|
||||||
import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException;
|
import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -53,6 +56,8 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
|
|||||||
@UniqueConstraint(columnNames = { "external_id" }, name = "sa_external_id_UNIQUE") })
|
@UniqueConstraint(columnNames = { "external_id" }, name = "sa_external_id_UNIQUE") })
|
||||||
public class SavingsAccount extends AbstractPersistable<Long> {
|
public class SavingsAccount extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(SavingsAccount.class);
|
||||||
|
|
||||||
@Column(name = "account_no", length = 20, unique = true, nullable = false)
|
@Column(name = "account_no", length = 20, unique = true, nullable = false)
|
||||||
private String accountNumber;
|
private String accountNumber;
|
||||||
|
|
||||||
@@ -123,7 +128,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
|||||||
private Integer lockinPeriodFrequencyType;
|
private Integer lockinPeriodFrequencyType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When account is becomes <code>active</code> this field is derived if
|
* When account becomes <code>active</code> this field is derived if
|
||||||
* <code>lockinPeriodFrequency</code> and
|
* <code>lockinPeriodFrequency</code> and
|
||||||
* <code>lockinPeriodFrequencyType</code> details are present.
|
* <code>lockinPeriodFrequencyType</code> details are present.
|
||||||
*/
|
*/
|
||||||
@@ -163,8 +168,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final String accountNo,
|
private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final String accountNo,
|
||||||
final String externalId, final SavingsAccountStatusType status, final LocalDate activationDate, final BigDecimal nominalAnnualInterestRate,
|
final String externalId, final SavingsAccountStatusType status, final LocalDate activationDate,
|
||||||
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
|
final BigDecimal nominalAnnualInterestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
|
||||||
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
|
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
|
||||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType) {
|
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType) {
|
||||||
@@ -278,15 +283,80 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
|||||||
public void postInterest(final LocalDate interestPostingUpToDate) {
|
public void postInterest(final LocalDate interestPostingUpToDate) {
|
||||||
|
|
||||||
final SavingsInterestPostingPeriodType postingPeriodType = SavingsInterestPostingPeriodType.fromInt(this.interestPostingPeriodType);
|
final SavingsInterestPostingPeriodType postingPeriodType = SavingsInterestPostingPeriodType.fromInt(this.interestPostingPeriodType);
|
||||||
List<LocalDate> postingLocalDates = determineInterestPostingDates(getActivationLocalDate(), interestPostingUpToDate,
|
final List<LocalDate> postingLocalDates = determineInterestPostingDates(getActivationLocalDate(), interestPostingUpToDate,
|
||||||
postingPeriodType);
|
postingPeriodType);
|
||||||
|
|
||||||
List<InterestCompoundingPeriodSummary> compoundingPeriods = calculateInterest(interestPostingUpToDate);
|
final List<InterestCompoundingPeriodSummary> compoundingPeriods = calculateInterest(interestPostingUpToDate);
|
||||||
|
|
||||||
//
|
Money interestPostedToDate = Money.zero(this.currency);
|
||||||
for (InterestCompoundingPeriodSummary interestCompoundingPeriodSummary : compoundingPeriods) {
|
|
||||||
|
|
||||||
|
boolean recalucateDailyBalanceDetails = false;
|
||||||
|
for (LocalDate postingDate : postingLocalDates) {
|
||||||
|
Money interestEarnedToBePostedForPeriod = findInterestEarnedToBePostedOn(interestPostedToDate, postingDate, compoundingPeriods);
|
||||||
|
|
||||||
|
interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod);
|
||||||
|
|
||||||
|
SavingsAccountTransaction postingTransaction = findInterestPostingTransactionFor(postingDate);
|
||||||
|
if (postingTransaction == null) {
|
||||||
|
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, postingDate,
|
||||||
|
interestEarnedToBePostedForPeriod);
|
||||||
|
this.transactions.add(newPostingTransaction);
|
||||||
|
recalucateDailyBalanceDetails = true;
|
||||||
|
} else {
|
||||||
|
boolean correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod);
|
||||||
|
if (correctionRequired) {
|
||||||
|
postingTransaction.reverse();
|
||||||
|
final SavingsAccountTransaction newPostingTransaction = SavingsAccountTransaction.interestPosting(this, postingDate,
|
||||||
|
interestEarnedToBePostedForPeriod);
|
||||||
|
this.transactions.add(newPostingTransaction);
|
||||||
|
recalucateDailyBalanceDetails = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recalucateDailyBalanceDetails) {
|
||||||
|
// no openingBalance concept supported yet but probably will to
|
||||||
|
// allow
|
||||||
|
// for migrations.
|
||||||
|
final Money openingAccountBalance = Money.zero(this.currency);
|
||||||
|
|
||||||
|
// update existing transactions so derived balance fields are
|
||||||
|
// correct.
|
||||||
|
recalculateDailyBalances(openingAccountBalance);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SavingsAccountTransaction findInterestPostingTransactionFor(final LocalDate postingDate) {
|
||||||
|
|
||||||
|
SavingsAccountTransaction postingTransation = null;
|
||||||
|
|
||||||
|
for (SavingsAccountTransaction transaction : this.transactions) {
|
||||||
|
if (transaction.isInterestPosting() && transaction.occursOn(postingDate)) {
|
||||||
|
postingTransation = transaction;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return postingTransation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Money findInterestEarnedToBePostedOn(final Money interestPostedToDate, final LocalDate postingDate,
|
||||||
|
final List<InterestCompoundingPeriodSummary> compoundingPeriods) {
|
||||||
|
|
||||||
|
Money interestToBePosted = Money.zero(this.currency);
|
||||||
|
|
||||||
|
BigDecimal lastRelevantInterest = BigDecimal.ZERO;
|
||||||
|
for (InterestCompoundingPeriodSummary interestCompoundingPeriodSummary : compoundingPeriods) {
|
||||||
|
if (interestCompoundingPeriodSummary.fallsBefore(postingDate)) {
|
||||||
|
interestToBePosted = Money.of(this.currency, interestCompoundingPeriodSummary.compoundedInterest());
|
||||||
|
lastRelevantInterest = interestCompoundingPeriodSummary.compoundedInterest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("Interest is: " + lastRelevantInterest + " :>> ");
|
||||||
|
|
||||||
|
return interestToBePosted.minus(interestPostedToDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LocalDate> determineInterestPostingDates(final LocalDate activationLocalDate, final LocalDate interestPostingUpToDate,
|
private List<LocalDate> determineInterestPostingDates(final LocalDate activationLocalDate, final LocalDate interestPostingUpToDate,
|
||||||
@@ -300,7 +370,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
|||||||
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
|
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
|
||||||
|
|
||||||
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType);
|
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType);
|
||||||
postingDates.add(interestPostingLocalDate);
|
if (!interestPostingLocalDate.isAfter(DateUtils.getLocalDateOfTenant())) {
|
||||||
|
postingDates.add(interestPostingLocalDate);
|
||||||
|
}
|
||||||
|
|
||||||
periodEndDate = interestPostingLocalDate;
|
periodEndDate = interestPostingLocalDate;
|
||||||
periodStartDate = interestPostingLocalDate;
|
periodStartDate = interestPostingLocalDate;
|
||||||
@@ -558,11 +630,25 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
|||||||
return periodsInOneYear;
|
return periodsInOneYear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<SavingsAccountTransaction> retreiveListOfTransactions() {
|
||||||
|
List<SavingsAccountTransaction> listOfTransactionsSorted = new ArrayList<SavingsAccountTransaction>();
|
||||||
|
for (SavingsAccountTransaction transaction : this.transactions) {
|
||||||
|
listOfTransactionsSorted.add(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
SavingsAccountTransactionComparator transactionComparator = new SavingsAccountTransactionComparator();
|
||||||
|
Collections.sort(listOfTransactionsSorted, transactionComparator);
|
||||||
|
|
||||||
|
return listOfTransactionsSorted;
|
||||||
|
}
|
||||||
|
|
||||||
private void recalculateDailyBalances(final Money openingAccountBalance) {
|
private void recalculateDailyBalances(final Money openingAccountBalance) {
|
||||||
|
|
||||||
Money runningBalance = openingAccountBalance.copy();
|
Money runningBalance = openingAccountBalance.copy();
|
||||||
for (SavingsAccountTransaction transaction : this.transactions) {
|
|
||||||
|
|
||||||
|
List<SavingsAccountTransaction> accountTransactionsSorted = retreiveListOfTransactions();
|
||||||
|
|
||||||
|
for (SavingsAccountTransaction transaction : accountTransactionsSorted) {
|
||||||
if (transaction.isReversed()) {
|
if (transaction.isReversed()) {
|
||||||
transaction.zeroBalanceFields();
|
transaction.zeroBalanceFields();
|
||||||
} else {
|
} else {
|
||||||
@@ -582,13 +668,13 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
// loop over transactions in reverse
|
// loop over transactions in reverse
|
||||||
LocalDate endOfBalanceDate = DateUtils.getLocalDateOfTenant();
|
LocalDate endOfBalanceDate = DateUtils.getLocalDateOfTenant();
|
||||||
for (int i = this.transactions.size() - 1; i >= 0; i--) {
|
for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) {
|
||||||
SavingsAccountTransaction transaction = this.transactions.get(i);
|
SavingsAccountTransaction transaction = accountTransactionsSorted.get(i);
|
||||||
transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate);
|
transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate);
|
||||||
|
|
||||||
// this transactions transaction date is end of balance date for
|
// this transactions transaction date is end of balance date for
|
||||||
// previous transaction.
|
// previous transaction.
|
||||||
endOfBalanceDate = transaction.localDate().minusDays(1);
|
endOfBalanceDate = transaction.transactionLocalDate().minusDays(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-4
@@ -76,6 +76,12 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
|||||||
return new SavingsAccountTransaction(savingsAccount, SavingsAccountTransactionType.WITHDRAWAL.getValue(), date, amount, isReversed);
|
return new SavingsAccountTransaction(savingsAccount, SavingsAccountTransactionType.WITHDRAWAL.getValue(), date, amount, isReversed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final LocalDate date, final Money amount) {
|
||||||
|
final boolean isReversed = false;
|
||||||
|
return new SavingsAccountTransaction(savingsAccount, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date, amount,
|
||||||
|
isReversed);
|
||||||
|
}
|
||||||
|
|
||||||
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Integer typeOf, final LocalDate transactionLocalDate,
|
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Integer typeOf, final LocalDate transactionLocalDate,
|
||||||
final Money amount, final boolean isReversed) {
|
final Money amount, final boolean isReversed) {
|
||||||
this.savingsAccount = savingsAccount;
|
this.savingsAccount = savingsAccount;
|
||||||
@@ -85,7 +91,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
|||||||
this.reversed = isReversed;
|
this.reversed = isReversed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate localDate() {
|
public LocalDate transactionLocalDate() {
|
||||||
return new LocalDate(this.dateOf);
|
return new LocalDate(this.dateOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +112,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInterestPosting() {
|
public boolean isInterestPosting() {
|
||||||
return SavingsAccountTransactionType.fromInt(this.typeOf).isInterestPosting();
|
return SavingsAccountTransactionType.fromInt(this.typeOf).isInterestPosting() && isNotReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotReversed() {
|
public boolean isNotReversed() {
|
||||||
@@ -117,6 +123,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
|||||||
return this.reversed;
|
return this.reversed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean occursOn(final LocalDate occursOnDate) {
|
||||||
|
return getTransactionLocalDate().isEqual(occursOnDate);
|
||||||
|
}
|
||||||
|
|
||||||
public void zeroBalanceFields() {
|
public void zeroBalanceFields() {
|
||||||
this.runningBalance = null;
|
this.runningBalance = null;
|
||||||
this.cumulativeBalance = null;
|
this.cumulativeBalance = null;
|
||||||
@@ -130,8 +140,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
|
public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
|
||||||
this.balanceEndDate = endOfBalanceDate.toDate();
|
this.balanceEndDate = endOfBalanceDate.toDate();
|
||||||
this.balanceNumberOfDays = LocalDateInterval.create(getTransactionLocalDate(), endOfBalanceDate)
|
this.balanceNumberOfDays = LocalDateInterval.create(getTransactionLocalDate(), endOfBalanceDate).daysInPeriodInclusiveOfEndDate();
|
||||||
.daysInPeriodInclusiveOfEndDate();
|
|
||||||
this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
|
this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,4 +184,9 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
|||||||
final LocalDateInterval interestPeriodInterval = LocalDateInterval.create(getTransactionLocalDate(), balanceValidTo);
|
final LocalDateInterval interestPeriodInterval = LocalDateInterval.create(getTransactionLocalDate(), balanceValidTo);
|
||||||
return SavingsAccountDailyBalance.createFrom(interestPeriodInterval, this.runningBalance, compoundedInterestToDate);
|
return SavingsAccountDailyBalance.createFrom(interestPeriodInterval, this.runningBalance, compoundedInterestToDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasNotAmount(final Money amountToCheck) {
|
||||||
|
final Money transactionAmount = getAmount(amountToCheck.getCurrency());
|
||||||
|
return transactionAmount.isNotEqualTo(amountToCheck);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort savings account transactions by transaction date and transaction type
|
||||||
|
* placing
|
||||||
|
*/
|
||||||
|
public class SavingsAccountTransactionComparator implements Comparator<SavingsAccountTransaction> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(final SavingsAccountTransaction o1, final SavingsAccountTransaction o2) {
|
||||||
|
int compareResult = 0;
|
||||||
|
final int comparsion = o1.transactionLocalDate().compareTo(o2.transactionLocalDate());
|
||||||
|
if (comparsion == 0) {
|
||||||
|
// equal transaction dates
|
||||||
|
if (o1.isInterestPosting() && !o2.isInterestPosting()) {
|
||||||
|
compareResult = -1;
|
||||||
|
} else if (!o1.isInterestPosting() && o2.isInterestPosting()) {
|
||||||
|
compareResult = 1;
|
||||||
|
} else {
|
||||||
|
compareResult = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
compareResult = comparsion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return compareResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -289,7 +289,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
@Override
|
@Override
|
||||||
public Collection<SavingsAccountTransactionData> retrieveAllTransactions(final Long savingsId) {
|
public Collection<SavingsAccountTransactionData> retrieveAllTransactions(final Long savingsId) {
|
||||||
|
|
||||||
final String sql = "select " + this.transactionsMapper.schema() + " where sa.id = ?";
|
final String sql = "select " + this.transactionsMapper.schema()
|
||||||
|
+ " where sa.id = ? and tr.is_reversed=0 order by tr.transaction_date DESC, tr.id DESC";
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId });
|
return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId });
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -333,6 +333,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult postInterest(final Long savingsId, final JsonCommand command) {
|
public CommandProcessingResult postInterest(final Long savingsId, final JsonCommand command) {
|
||||||
this.context.authenticatedUser();
|
this.context.authenticatedUser();
|
||||||
|
|||||||
Reference in New Issue
Block a user