This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new d75835170 FINERACT-1330: Pay Charge & withdrawals is violating
Overdraft conditions on current and past Transactions
d75835170 is described below
commit d75835170a5ce2f05c0ef295fb2e8abd247f1e9c
Author: rrpawar96 <[email protected]>
AuthorDate: Sat Jul 16 01:43:37 2022 +0530
FINERACT-1330: Pay Charge & withdrawals is violating Overdraft conditions
on current and past Transactions
---
.../portfolio/savings/domain/SavingsAccount.java | 19 +++++--
.../SavingsAccountTransactionRepository.java | 8 ++-
...countWritePlatformServiceJpaRepositoryImpl.java | 13 ++++-
.../ClientSavingsIntegrationTest.java | 58 ++++++++++++++++++++++
.../common/savings/SavingsAccountHelper.java | 7 +++
5 files changed, 99 insertions(+), 6 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
index 79cba8ee1..6fac36276 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
@@ -1521,6 +1521,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
final List<DepositAccountOnHoldTransaction>
depositAccountOnHoldTransactions, final boolean backdatedTxnsAllowedTill) {
List<SavingsAccountTransaction> transactionsSortedByDate = null;
+ BigDecimal transactionAmount = null;
if (backdatedTxnsAllowedTill) {
transactionsSortedByDate = retrieveSortedTransactions();
} else {
@@ -1535,6 +1536,9 @@ public class SavingsAccount extends
AbstractPersistableCustom {
Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
LocalDate lastSavingsDate = null;
for (final SavingsAccountTransaction transaction :
transactionsSortedByDate) {
+
+ transactionAmount = transaction.getAmount();
+
if (transaction.isNotReversed() && transaction.isCredit()) {
runningBalance =
runningBalance.plus(transaction.getAmount(this.currency));
} else if (transaction.isNotReversed() && transaction.isDebit()) {
@@ -1573,14 +1577,11 @@ public class SavingsAccount extends
AbstractPersistableCustom {
throw new
PlatformApiDataValidationException(dataValidationErrors);
}
}
-
}
lastSavingsDate = transaction.transactionLocalDate();
-
}
BigDecimal withdrawalFee = null;
- BigDecimal transactionAmount = null;
if (isOverdraft()) {
if (runningBalance.minus(minRequiredBalance).isLessThanZero()) {
throw new
InsufficientAccountBalanceException("transactionAmount", getAccountBalance(),
withdrawalFee, transactionAmount);
@@ -1588,6 +1589,18 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
}
+ public void validateAccountBalanceDoesNotViolateOverdraft(final
List<SavingsAccountTransaction> savingsAccountTransaction,
+ final BigDecimal amountPaid) {
+ if (savingsAccountTransaction != null) {
+ SavingsAccountTransaction savingsAccountTransactionFirst =
savingsAccountTransaction.get(0);
+ if (!this.allowOverdraft) {
+ if
(savingsAccountTransactionFirst.getRunningBalance(this.currency).minus(amountPaid).isLessThanZero())
{
+ throw new
InsufficientAccountBalanceException("transactionAmount", getAccountBalance(),
null, amountPaid);
+ }
+ }
+ }
+ }
+
protected boolean isAccountLocked(final LocalDate transactionDate) {
boolean isLocked = false;
final boolean accountHasLockedInSetting = this.lockedInUntilDate !=
null;
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
index 6d2db4080..e0d03b657 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.savings.domain;
import java.time.LocalDate;
import java.util.List;
import javax.persistence.LockModeType;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Lock;
@@ -47,6 +48,9 @@ public interface SavingsAccountTransactionRepository
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<SavingsAccountTransaction>
findBySavingsAccount(@Param("savingsAccount") SavingsAccount savingsAccount);
- @Query("select sat from SavingsAccountTransaction sat where sat.refNo =
:refNo")
- List<SavingsAccountTransaction> findAllTransactionByRefNo(@Param("refNo")
String refNo);
+ List<SavingsAccountTransaction> findByRefNo(@Param("refNo") String refNo);
+
+ @Query("select sat from SavingsAccountTransaction sat where
sat.savingsAccount.id = :savingsId and sat.dateOf <= :transactionDate and
sat.reversed=false")
+ List<SavingsAccountTransaction>
findBySavingsAccountIdAndLessThanDateOfAndReversedIsFalse(@Param("savingsId")
Long savingsId,
+ @Param("transactionDate") LocalDate transactionDate, Pageable
pageable);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
index fc17fd9bb..89e94e749 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -130,6 +130,9 @@ import
org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
@@ -718,7 +721,7 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
List<SavingsAccountTransaction> savingsAccountTransactions = null;
if (isBulk) {
String transactionRefNo = savingsAccountTransaction.getRefNo();
- savingsAccountTransactions =
this.savingsAccountTransactionRepository.findAllTransactionByRefNo(transactionRefNo);
+ savingsAccountTransactions =
this.savingsAccountTransactionRepository.findByRefNo(transactionRefNo);
reversal =
this.savingsAccountDomainService.handleReversal(account,
savingsAccountTransactions, backdatedTxnsAllowedTill);
} else {
reversal =
this.savingsAccountDomainService.handleReversal(account,
Collections.singletonList(savingsAccountTransaction),
@@ -1471,6 +1474,13 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingAccountAssembler.assignSavingAccountHelpers(account);
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
+ Pageable sortedByDateAndIdDesc = PageRequest.of(0, 1,
Sort.by("dateOf", "id").descending());
+
+ List<SavingsAccountTransaction> savingsAccountTransaction =
this.savingsAccountTransactionRepository
+
.findBySavingsAccountIdAndLessThanDateOfAndReversedIsFalse(account.getId(),
transactionDate, sortedByDateAndIdDesc);
+
+
account.validateAccountBalanceDoesNotViolateOverdraft(savingsAccountTransaction,
amountPaid);
+
updateExistingTransactionsDetails(account, existingTransactionIds,
existingReversedTransactionIds);
SavingsAccountTransaction chargeTransaction =
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter,
user,
backdatedTxnsAllowedTill, null);
@@ -1488,6 +1498,7 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
financialYearBeginningMonth, postInterestOnDate,
backdatedTxnsAllowedTill, postReversals);
}
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions
= null;
+
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) > 0) {
depositAccountOnHoldTransactions =
this.depositAccountOnHoldTransactionRepository
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java
index 9788a93a9..5e5b65551 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java
@@ -2869,6 +2869,64 @@ public class ClientSavingsIntegrationTest {
assertEquals(balance.toString(),
requestedTransaction.get("runningBalance").toString(), "Equality check for
Balance");
}
+ @Test
+ public void testSavingsAccountChargesBackDate() {
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
+ SavingsAccountHelper savingsAccountHelperValidationError = new
SavingsAccountHelper(this.requestSpec,
+ new ResponseSpecBuilder().build());
+
+ final Integer clientID = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec,
this.responseSpec, clientID);
+
+ final String minBalanceForInterestCalculation = null;
+ final String minRequiredBalance = null;
+ final String enforceMinRequiredBalance = "false";
+ final boolean allowOverdraft = false;
+ final Integer savingsProductID =
createSavingsProduct(this.requestSpec, this.responseSpec, "0",
minBalanceForInterestCalculation,
+ minRequiredBalance, enforceMinRequiredBalance, allowOverdraft);
+ Assertions.assertNotNull(savingsProductID);
+
+ final Integer savingsId =
this.savingsAccountHelper.applyForSavingsApplication(clientID,
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
+ Assertions.assertNotNull(savingsProductID);
+
+ HashMap modifications =
this.savingsAccountHelper.updateSavingsAccount(clientID, savingsProductID,
savingsId,
+ ACCOUNT_TYPE_INDIVIDUAL);
+ Assertions.assertTrue(modifications.containsKey("submittedOnDate"));
+
+ HashMap savingsStatusHashMap =
SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec,
savingsId);
+ SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
+
+ savingsStatusHashMap =
this.savingsAccountHelper.approveSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+
+ savingsStatusHashMap =
this.savingsAccountHelper.activateSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+ final Integer chargeId = ChargesHelper.createCharges(this.requestSpec,
this.responseSpec,
+ ChargesHelper.getSavingsSpecifiedDueDateJSON());
+ Assertions.assertNotNull(chargeId);
+
+ ArrayList<HashMap> charges =
this.savingsAccountHelper.getSavingsCharges(savingsId);
+ Assertions.assertTrue(charges == null || charges.size() == 0);
+
+ this.savingsAccountHelper.depositToSavingsAccount(savingsId, "100",
"05 March 2013", CommonConstants.RESPONSE_RESOURCE_ID);
+
+ this.savingsAccountHelper.depositToSavingsAccount(savingsId, "100",
"07 March 2013", CommonConstants.RESPONSE_RESOURCE_ID);
+
+ final Integer savingsChargeId =
this.savingsAccountHelper.addChargesForSavingsWithDueDate(savingsId, chargeId,
"07 March 2013",
+ 200);
+
+ ArrayList<HashMap> savingsAccountErrorData = (ArrayList<HashMap>)
savingsAccountHelperValidationError
+ .payChargeToSavingsAccount(savingsId, savingsChargeId, "200",
"06 March 2013", CommonConstants.RESPONSE_ERROR);
+
+
assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
+
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+
+ final Integer payChargeId =
this.savingsAccountHelper.payCharge(savingsChargeId, savingsId, "200", "07
March 2013");
+
+ Assertions.assertNotNull(payChargeId);
+ }
+
@Test
public void testRunningBalanceAfterWithdrawalWithBackdateConfigurationOn()
{
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
index e76abd6f6..8ce6a5b5b 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
@@ -263,6 +263,13 @@ public class SavingsAccountHelper {
getSavingsTransactionPaymentTypeJSON(amount, date,
paymentType), jsonAttributeToGetback);
}
+ public Object payChargeToSavingsAccount(final Integer savingsID, final
Integer chargeId, final String amount, String date,
+ String jsonAttributeToGetback) {
+ LOG.info("--------------------------------- PAY SAVINGS CHARGE
--------------------------------");
+ return performSavingActions(createChargesURL("paycharge", savingsID,
chargeId), getSavingsPayChargeJSON(amount, date),
+ jsonAttributeToGetback);
+ }
+
public Integer updateSavingsAccountTransaction(final Integer savingsId,
final Integer transactionId, final String amount) {
LOG.info("\n--------------------------------- MODIFY SAVINGS
TRANSACTION --------------------------------");
return (Integer)
performSavingActions(createAdjustTransactionURL(MODIFY_TRASACTION_COMMAND,
savingsId, transactionId),