FINERACT-1330: Pay Charge & withdrawals is violating Overdraft conditions on current and past Transactions
This commit is contained in:
committed by
Arnold Gálovics
parent
ed80540a95
commit
d75835170a
+16
-3
@@ -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;
|
||||
|
||||
+6
-2
@@ -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);
|
||||
}
|
||||
|
||||
+12
-1
@@ -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);
|
||||
|
||||
+58
@@ -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);
|
||||
|
||||
+7
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user