This is an automated email from the ASF dual-hosted git repository.
adamsaghy 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 2ac1445dba Confirm existance of elements before accessing them
2ac1445dba is described below
commit 2ac1445dbaea3457cfa5e298f4e9f29707a6c150
Author: Wilfred Kigenyi <[email protected]>
AuthorDate: Mon Dec 2 00:38:11 2024 +0300
Confirm existance of elements before accessing them
---
.../portfolio/savings/domain/SavingsAccount.java | 7 +-
.../ClientSavingsIntegrationTest.java | 314 +++++++++++++++++++++
2 files changed, 320 insertions(+), 1 deletion(-)
diff --git
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
index bfd94a1ead..59bba2100a 100644
---
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
+++
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
@@ -1593,13 +1593,18 @@ public class SavingsAccount extends
AbstractAuditableWithUTCDateTimeCustom<Long>
public void validateAccountBalanceDoesNotViolateOverdraft(final
List<SavingsAccountTransaction> savingsAccountTransaction,
final BigDecimal amountPaid) {
- if (savingsAccountTransaction != null) {
+ if (savingsAccountTransaction != null &&
savingsAccountTransaction.size() > 0) {
SavingsAccountTransaction savingsAccountTransactionFirst =
savingsAccountTransaction.get(0);
if (!this.allowOverdraft) {
if
(savingsAccountTransactionFirst.getRunningBalance(this.currency).minus(amountPaid).isLessThanZero())
{
throw new
InsufficientAccountBalanceException("transactionAmount", getAccountBalance(),
null, amountPaid);
}
}
+
+ } else {
+ if (!this.allowOverdraft) {
+ throw new
InsufficientAccountBalanceException("transactionAmount", BigDecimal.ZERO, null,
amountPaid);
+ }
}
}
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 115ce14b43..15d6655c40 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
@@ -3184,6 +3184,320 @@ public class ClientSavingsIntegrationTest {
error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
}
+ @Test
+ public void
testSavingsAccountWithdrawalWithoutPriorTransactionsWithoutOverdraft() {
+ final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd
MMMM yyyy", Locale.US);
+
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
+ final ResponseSpecification errorResponse = new
ResponseSpecBuilder().expectStatusCode(403).build();
+ final SavingsAccountHelper validationErrorHelper = new
SavingsAccountHelper(this.requestSpec, errorResponse);
+
+ /***
+ * Create a client to apply for savings account (overdraft account).
+ */
+ final Integer clientID = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec,
this.responseSpec, clientID);
+ // Assertions.assertNotNull(clientID);
+ final String minBalanceForInterestCalculation = null;
+
+ /***
+ * Create savings product with zero opening balance and overdraft
disabled
+ */
+ final String zeroOpeningBalance = "0.0";
+ final String minRequiredBalance = null;
+ final String enforceMinRequiredBalance = "false";
+ final boolean allowOverdraft = false;
+ final Integer savingsProductID =
createSavingsProduct(this.requestSpec, this.responseSpec, zeroOpeningBalance,
+ minBalanceForInterestCalculation, minRequiredBalance,
enforceMinRequiredBalance, allowOverdraft);
+ Assertions.assertNotNull(savingsProductID);
+
+ /***
+ * Apply for Savings account
+ */
+ 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);
+
+ /***
+ * Approve the savings account
+ */
+ savingsStatusHashMap =
this.savingsAccountHelper.approveSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+
+ LocalDate todaysDate = Utils.getLocalDateOfTenant();
+ todaysDate = todaysDate.minusMonths(1);
+ todaysDate = todaysDate.withDayOfMonth(1);
+ final String ACTIVATION_DATE = dateFormat.format(todaysDate);
+ final Integer lastDayOfMonth = todaysDate.lengthOfMonth();
+ todaysDate = todaysDate.withDayOfMonth(lastDayOfMonth);
+ final String TRANSACTION_DATE = dateFormat.format(todaysDate);
+
+ /***
+ * Activate the application and verify account status
+ *
+ * @param activationDate
+ * this value is every time first day of previous month
+ */
+ savingsStatusHashMap = activateSavingsAccount(savingsId,
ACTIVATION_DATE);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+ Float balance = Float.parseFloat(zeroOpeningBalance);
+
+ /***
+ * Attempt a withdrawal transaction, verify an exception will be
thrown since there are no prior transactions to
+ * this transaction
+ */
+ ArrayList<HashMap> savingsAccountErrorData = (ArrayList<HashMap>)
validationErrorHelper.withdrawalFromSavingsAccount(savingsId,
+ WITHDRAW_AMOUNT, TRANSACTION_DATE,
CommonConstants.RESPONSE_ERROR);
+
assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
+
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+ }
+
+ @Test
+ public void
testWithdrawalWithPriorTransactionsWithOverdraft_AMT_GT_Balance() {
+ final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd
MMMM yyyy", Locale.US);
+
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
+ final ResponseSpecification errorResponse = new
ResponseSpecBuilder().expectStatusCode(400).build();
+ final SavingsAccountHelper validationErrorHelper = new
SavingsAccountHelper(this.requestSpec, errorResponse);
+
+ /***
+ * Create a client to apply for savings account (overdraft account).
+ */
+ final Integer clientID = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec,
this.responseSpec, clientID);
+ // Assertions.assertNotNull(clientID);
+ final String minBalanceForInterestCalculation = null;
+
+ /***
+ * Create savings product with zero opening balance and overdraft
enabled
+ */
+ final String nonZeroOpeningBalance = "500.00";
+ final String minRequiredBalance = null;
+ final String enforceMinRequiredBalance = "false";
+ final boolean allowOverdraft = true;
+ final Integer savingsProductID =
createSavingsProduct(this.requestSpec, this.responseSpec, nonZeroOpeningBalance,
+ minBalanceForInterestCalculation, minRequiredBalance,
enforceMinRequiredBalance, allowOverdraft);
+ Assertions.assertNotNull(savingsProductID);
+
+ /***
+ * Apply for Savings account
+ */
+ 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);
+
+ /***
+ * Approve the savings account
+ */
+ savingsStatusHashMap =
this.savingsAccountHelper.approveSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+
+ LocalDate todaysDate = Utils.getLocalDateOfTenant();
+ todaysDate = todaysDate.minusMonths(1);
+ todaysDate = todaysDate.withDayOfMonth(1);
+ final String ACTIVATION_DATE = dateFormat.format(todaysDate);
+ final Integer lastDayOfMonth = todaysDate.lengthOfMonth();
+ todaysDate = todaysDate.withDayOfMonth(lastDayOfMonth);
+
+ /***
+ * Activate the application and verify account status
+ *
+ * @param activationDate
+ * this value is every time first day of previous month
+ */
+ savingsStatusHashMap = activateSavingsAccount(savingsId,
ACTIVATION_DATE);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+ Float balance = Float.parseFloat(nonZeroOpeningBalance);
+
+ /***
+ * Attempt a withdrawal transaction, verify an exception will be
thrown since there are no prior transactions to
+ * this transaction
+ */
+ /***
+ * Perform withdraw transaction, verify account balance(account
balance will go to negative as no deposits are
+ * there prior to this transaction)
+ */
+ Integer withdrawTransactionId = (Integer)
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId,
WITHDRAW_AMOUNT,
+ ACTIVATION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+ HashMap withdrawTransaction =
this.savingsAccountHelper.getSavingsTransaction(savingsId,
withdrawTransactionId);
+ balance -= Float.parseFloat(WITHDRAW_AMOUNT);
+ assertEquals(Float.parseFloat(WITHDRAW_AMOUNT),
withdrawTransaction.get("amount"), "Verifying Withdrawal Amount");
+ assertEquals(balance, withdrawTransaction.get("runningBalance"),
"Verifying Balance after Withdrawal");
+
+ }
+
+ @Test
+ public void
testWithdrawalWithPriorTransactionsWithOverdraft_AMT_LT_Balance() {
+ final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd
MMMM yyyy", Locale.US);
+
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
+ final ResponseSpecification errorResponse = new
ResponseSpecBuilder().expectStatusCode(400).build();
+ final SavingsAccountHelper validationErrorHelper = new
SavingsAccountHelper(this.requestSpec, errorResponse);
+
+ /***
+ * Create a client to apply for savings account (overdraft account).
+ */
+ final Integer clientID = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec,
this.responseSpec, clientID);
+ // Assertions.assertNotNull(clientID);
+ final String minBalanceForInterestCalculation = null;
+
+ /***
+ * Create savings product with zero opening balance and overdraft
enabled
+ */
+ final String nonZeroOpeningBalance = "2000";
+ final String minRequiredBalance = null;
+ final String enforceMinRequiredBalance = "false";
+ final boolean allowOverdraft = false;
+ final Integer savingsProductID =
createSavingsProduct(this.requestSpec, this.responseSpec, nonZeroOpeningBalance,
+ minBalanceForInterestCalculation, minRequiredBalance,
enforceMinRequiredBalance, allowOverdraft);
+ Assertions.assertNotNull(savingsProductID);
+
+ /***
+ * Apply for Savings account
+ */
+ 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);
+
+ /***
+ * Approve the savings account
+ */
+ savingsStatusHashMap =
this.savingsAccountHelper.approveSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+
+ LocalDate todaysDate = Utils.getLocalDateOfTenant();
+ todaysDate = todaysDate.minusMonths(1);
+ todaysDate = todaysDate.withDayOfMonth(1);
+ final String ACTIVATION_DATE = dateFormat.format(todaysDate);
+
+ /***
+ * Activate the application and verify account status
+ *
+ * @param activationDate
+ * this value is every time first day of previous month
+ */
+ savingsStatusHashMap = activateSavingsAccount(savingsId,
ACTIVATION_DATE);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+ Float balance = Float.parseFloat(nonZeroOpeningBalance);
+
+ /***
+ * Attempt a withdrawal transaction, verify an exception will be
thrown since there are no prior transactions to
+ * this transaction
+ */
+ /***
+ * Perform withdraw transaction, verify account balance(account
balance will go to negative as no deposits are
+ * there prior to this transaction)
+ */
+ Integer withdrawTransactionId = (Integer)
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId,
WITHDRAW_AMOUNT,
+ ACTIVATION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+ HashMap withdrawTransaction =
this.savingsAccountHelper.getSavingsTransaction(savingsId,
withdrawTransactionId);
+ balance -= Float.parseFloat(WITHDRAW_AMOUNT);
+ assertEquals(Float.parseFloat(WITHDRAW_AMOUNT),
withdrawTransaction.get("amount"), "Verifying Withdrawal Amount");
+ assertEquals(balance, withdrawTransaction.get("runningBalance"),
"Verifying Balance after Withdrawal");
+
+ }
+
+ @Test
+ public void
testWithdrawalWithPriorTransactionsWithOverdraft_AMT_EQ_Balance() {
+ final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd
MMMM yyyy", Locale.US);
+
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
+ final ResponseSpecification errorResponse = new
ResponseSpecBuilder().expectStatusCode(400).build();
+ final SavingsAccountHelper validationErrorHelper = new
SavingsAccountHelper(this.requestSpec, errorResponse);
+
+ /***
+ * Create a client to apply for savings account (overdraft account).
+ */
+ final Integer clientID = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec,
this.responseSpec, clientID);
+ // Assertions.assertNotNull(clientID);
+ final String minBalanceForInterestCalculation = null;
+
+ /***
+ * Create savings product with zero opening balance and overdraft
enabled
+ */
+ final String nonZeroOpeningBalance = "1000";
+ final String minRequiredBalance = null;
+ final String enforceMinRequiredBalance = "false";
+ final boolean allowOverdraft = false;
+ final Integer savingsProductID =
createSavingsProduct(this.requestSpec, this.responseSpec, nonZeroOpeningBalance,
+ minBalanceForInterestCalculation, minRequiredBalance,
enforceMinRequiredBalance, allowOverdraft);
+ Assertions.assertNotNull(savingsProductID);
+
+ /***
+ * Apply for Savings account
+ */
+ 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);
+
+ /***
+ * Approve the savings account
+ */
+ savingsStatusHashMap =
this.savingsAccountHelper.approveSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+
+ LocalDate todaysDate = Utils.getLocalDateOfTenant();
+ todaysDate = todaysDate.minusMonths(1);
+ todaysDate = todaysDate.withDayOfMonth(1);
+ final String ACTIVATION_DATE = dateFormat.format(todaysDate);
+
+ /***
+ * Activate the application and verify account status
+ *
+ * @param activationDate
+ * this value is every time first day of previous month
+ */
+ savingsStatusHashMap = activateSavingsAccount(savingsId,
ACTIVATION_DATE);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+ Float balance = Float.parseFloat(nonZeroOpeningBalance);
+
+ /***
+ * Attempt a withdrawal transaction, verify an exception will be
thrown since there are no prior transactions to
+ * this transaction
+ */
+ /***
+ * Perform withdraw transaction, verify account balance(account
balance will go to negative as no deposits are
+ * there prior to this transaction)
+ */
+ Integer withdrawTransactionId = (Integer)
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId,
WITHDRAW_AMOUNT,
+ ACTIVATION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+ HashMap withdrawTransaction =
this.savingsAccountHelper.getSavingsTransaction(savingsId,
withdrawTransactionId);
+ balance -= Float.parseFloat(WITHDRAW_AMOUNT);
+ assertEquals(Float.parseFloat(WITHDRAW_AMOUNT),
withdrawTransaction.get("amount"), "Verifying Withdrawal Amount");
+ assertEquals(balance, withdrawTransaction.get("runningBalance"),
"Verifying Balance after Withdrawal");
+
+ }
+
private Integer createSavingsAccountDailyPostingOverdraft(final Integer
clientID, final String startDate) {
final Integer savingsProductID =
createSavingsProductDailyPostingOverdraft();
Assertions.assertNotNull(savingsProductID);