You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ma...@apache.org on 2022/06/17 10:12:13 UTC
[fineract] branch develop updated: FINERACT-1634: Invalid balance after withdrawal correction
This is an automated email from the ASF dual-hosted git repository.
manojvm 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 49e67a01c FINERACT-1634: Invalid balance after withdrawal correction
new 2a79a7ed2 Merge pull request #2353 from logoutdhaval/fineract-1634
49e67a01c is described below
commit 49e67a01c89835ff886bb2b4c6de7ffba6017a2f
Author: Dhaval Maniyar <dh...@Dhavals-MacBook-Pro.local>
AuthorDate: Thu Jun 9 17:42:24 2022 +0530
FINERACT-1634: Invalid balance after withdrawal correction
---
.../portfolio/savings/domain/SavingsAccount.java | 12 +-
.../SavingsAccountTransactionSummaryWrapper.java | 5 +-
...avingsAccountBalanceCheckAfterReversalTest.java | 142 +++++++++++++++++++++
3 files changed, 152 insertions(+), 7 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 821f58e53..ee20f96d2 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
@@ -975,12 +975,12 @@ public class SavingsAccount extends AbstractPersistableCustom {
boolean isTransactionsModified = false;
for (final SavingsAccountTransaction transaction : accountTransactionsSorted) {
- if (transaction.isReversed()) {
+ if (transaction.isReversed() || transaction.isReversalTransaction()) {
transaction.zeroBalanceFields();
} else {
Money overdraftAmount = Money.zero(this.currency);
Money transactionAmount = Money.zero(this.currency);
- if (transaction.isCredit() || transaction.isAmountRelease()) {
+ if ((transaction.isCredit() || transaction.isAmountRelease())) {
if (runningBalance.isLessThanZero()) {
Money diffAmount = transaction.getAmount(this.currency).plus(runningBalance);
if (diffAmount.isGreaterThanZero()) {
@@ -998,7 +998,9 @@ public class SavingsAccount extends AbstractPersistableCustom {
}
runningBalance = runningBalance.plus(transactionAmount);
- transaction.updateRunningBalance(runningBalance);
+ if (!transaction.getRunningBalance(transactionAmount.getCurrency()).isEqualTo(transactionAmount)) {
+ transaction.updateRunningBalance(runningBalance);
+ }
if (overdraftAmount.isZero() && runningBalance.isLessThanZero()) {
overdraftAmount = overdraftAmount.plus(runningBalance.getAmount().negate());
}
@@ -1407,9 +1409,9 @@ public class SavingsAccount extends AbstractPersistableCustom {
LocalDate lastSavingsDate = null;
final BigDecimal withdrawalFee = null;
for (final SavingsAccountTransaction transaction : transactionsSortedByDate) {
- if (transaction.isNotReversed() && transaction.isCredit()) {
+ if (transaction.isNotReversed() && transaction.isCredit() && !transaction.isReversalTransaction()) {
runningBalance = runningBalance.plus(transaction.getAmount(this.currency));
- } else if (transaction.isNotReversed() && transaction.isDebit()) {
+ } else if (transaction.isNotReversed() && transaction.isDebit() && !transaction.isReversalTransaction()) {
runningBalance = runningBalance.minus(transaction.getAmount(this.currency));
} else {
continue;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java
index 796653c83..1498c92cc 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java
@@ -36,7 +36,8 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalDeposits(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isDepositAndNotReversed() || transaction.isDividendPayoutAndNotReversed()) {
+ if ((transaction.isDepositAndNotReversed() || transaction.isDividendPayoutAndNotReversed())
+ && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -56,7 +57,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalWithdrawals(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isWithdrawal() && transaction.isNotReversed()) {
+ if (transaction.isWithdrawal() && transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountBalanceCheckAfterReversalTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountBalanceCheckAfterReversalTest.java
new file mode 100644
index 000000000..0e6c7e3a2
--- /dev/null
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountBalanceCheckAfterReversalTest.java
@@ -0,0 +1,142 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.HashMap;
+import java.util.List;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
+import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
+import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class SavingsAccountBalanceCheckAfterReversalTest {
+
+ public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
+ public static final String START_DATE = "10 April 2022";
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+ private SavingsProductHelper savingsProductHelper;
+ private SavingsAccountHelper savingsAccountHelper;
+ private SchedulerJobHelper scheduleJobHelper;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
+ this.savingsProductHelper = new SavingsProductHelper();
+ this.scheduleJobHelper = new SchedulerJobHelper(requestSpec);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testSavingsBalanceAfterWithdrawal() {
+ SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec,
+ new ResponseSpecBuilder().build());
+ final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, START_DATE);
+ Assertions.assertNotNull(clientID);
+ final Integer savingsId = createSavingsAccountDailyPosting(clientID);
+ Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "10000", START_DATE,
+ CommonConstants.RESPONSE_RESOURCE_ID);
+ this.savingsAccountHelper.reverseSavingsAccountTransaction(savingsId, depositTransactionId);
+ HashMap reversedDepositTransaction = this.savingsAccountHelper.getSavingsTransaction(savingsId, depositTransactionId);
+ Assertions.assertTrue((Boolean) reversedDepositTransaction.get("reversed"));
+ HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+ Float balance = Float.parseFloat("0.0");
+ assertEquals(balance, summary.get("accountBalance"), "Verifying opening Balance is 0");
+ List<HashMap> error = (List<HashMap>) savingsAccountHelperValidationError.withdrawalFromSavingsAccount(savingsId, "100", START_DATE,
+ CommonConstants.RESPONSE_ERROR);
+ assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
+ error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+ }
+
+ @Test
+ public void testSavingsBalanceWithOverDraftAfterWithdrawal() {
+ final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, START_DATE);
+ Assertions.assertNotNull(clientID);
+ final Integer savingsId = createSavingsAccountDailyPostingWithOverDraft(clientID);
+ Integer withdrawalTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "1000", START_DATE,
+ CommonConstants.RESPONSE_RESOURCE_ID);
+ this.savingsAccountHelper.reverseSavingsAccountTransaction(savingsId, withdrawalTransactionId);
+ HashMap reversedWithdrawalTransaction = this.savingsAccountHelper.getSavingsTransaction(savingsId, withdrawalTransactionId);
+ Assertions.assertTrue((Boolean) reversedWithdrawalTransaction.get("reversed"));
+ HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+ Float balance = Float.parseFloat("0.0");
+ assertEquals(balance, summary.get("accountBalance"), "Verifying Balance is 0");
+ Integer withdrawalAfterReversalTransactionId = (Integer) savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "500",
+ START_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+ summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+ balance = Float.parseFloat("-500.0");
+ assertEquals(balance, summary.get("accountBalance"), "Verifying Balance is -500");
+ }
+
+ private Integer createSavingsAccountDailyPosting(final Integer clientID) {
+ final Integer savingsProductID = createSavingsProductDailyPosting();
+ Assertions.assertNotNull(savingsProductID);
+ final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, savingsProductID,
+ ACCOUNT_TYPE_INDIVIDUAL, START_DATE);
+ Assertions.assertNotNull(savingsId);
+ HashMap savingsStatusHashMap = this.savingsAccountHelper.approveSavingsOnDate(savingsId, START_DATE);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+ savingsStatusHashMap = this.savingsAccountHelper.activateSavingsAccount(savingsId, START_DATE);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+ return savingsId;
+ }
+
+ private Integer createSavingsAccountDailyPostingWithOverDraft(final Integer clientID) {
+ final Integer savingsProductID = createSavingsProductDailyPostingWithOverDraft();
+ Assertions.assertNotNull(savingsProductID);
+ final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, savingsProductID,
+ ACCOUNT_TYPE_INDIVIDUAL, START_DATE);
+ Assertions.assertNotNull(savingsId);
+ HashMap savingsStatusHashMap = this.savingsAccountHelper.approveSavingsOnDate(savingsId, START_DATE);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+ savingsStatusHashMap = this.savingsAccountHelper.activateSavingsAccount(savingsId, START_DATE);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+ return savingsId;
+ }
+
+ private Integer createSavingsProductDailyPosting() {
+ final String savingsProductJSON = this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
+ .withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().build();
+ return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
+ }
+
+ private Integer createSavingsProductDailyPostingWithOverDraft() {
+ final String savingsProductJSON = this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
+ .withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().withOverDraft("10000").build();
+ return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
+ }
+
+}