You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by al...@apache.org on 2022/08/20 10:52:19 UTC

[fineract] branch 1.8.0 updated: FINERACT-1646-backdated-transaction-available-balance-fix

This is an automated email from the ASF dual-hosted git repository.

aleks pushed a commit to branch 1.8.0
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/1.8.0 by this push:
     new c3b462919 FINERACT-1646-backdated-transaction-available-balance-fix
c3b462919 is described below

commit c3b462919fed1206bd8a6d3255d3591f84970f2c
Author: Dhaval Maniyar <dh...@Dhavals-MacBook-Pro.local>
AuthorDate: Sat Jul 30 22:08:59 2022 +0530

    FINERACT-1646-backdated-transaction-available-balance-fix
---
 .../portfolio/savings/data/SavingsAccountData.java | 10 +++++++++
 .../savings/data/SavingsAccountSummaryData.java    | 11 ++++++++--
 .../savings/domain/SavingsAccountAssembler.java    | 25 +++++++++++++++++-----
 .../SavingsAccountInterestPostingServiceImpl.java  |  8 +++++--
 .../SavingsAccountReadPlatformServiceImpl.java     |  2 +-
 .../service/SavingsSchedularServiceImpl.java       |  2 +-
 .../ClientSavingsIntegrationTest.java              | 12 ++++++-----
 7 files changed, 54 insertions(+), 16 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
index 8f66a23cf..befcbe8de 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
@@ -116,6 +116,8 @@ public final class SavingsAccountData implements Serializable {
     private final BigDecimal minOverdraftForInterestCalculation;
     private transient List<SavingsAccountTransactionData> savingsAccountTransactionData = new ArrayList<>();
 
+    private transient SavingsAccountTransactionData lastSavingsAccountTransaction;
+
     private List<DatatableData> datatables = null;
 
     // import field
@@ -1104,4 +1106,12 @@ public final class SavingsAccountData implements Serializable {
     public Set<Long> getExistingReversedTransactionIds() {
         return this.existingReversedTransactionIds;
     }
+
+    public SavingsAccountTransactionData getLastSavingsAccountTransaction() {
+        return lastSavingsAccountTransaction;
+    }
+
+    public void setLastSavingsAccountTransaction(SavingsAccountTransactionData lastSavingsAccountTransaction) {
+        this.lastSavingsAccountTransaction = lastSavingsAccountTransaction;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java
index a7eea4d47..9335f8df6 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java
@@ -223,16 +223,23 @@ public class SavingsAccountSummaryData implements Serializable {
             Money interestTotal = Money.of(currency, this.totalInterestPosted);
             Money withHoldTaxTotal = Money.of(currency, this.totalWithholdTax);
             Money overdraftInterestTotal = Money.of(currency, this.totalOverdraftInterestDerived);
+            this.totalDeposits = wrapper.calculateTotalDeposits(currency, savingsAccountTransactions);
+            this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, savingsAccountTransactions);
             final HashMap<String, Money> map = updateRunningBalanceAndPivotDate(true, savingsAccountTransactions, interestTotal,
                     overdraftInterestTotal, withHoldTaxTotal, currency);
             interestTotal = map.get("interestTotal");
             withHoldTaxTotal = map.get("withHoldTax");
             overdraftInterestTotal = map.get("overdraftInterestTotal");
+            BigDecimal deltaInterest = interestTotal.minus(this.totalInterestPosted).getAmountDefaultedToNullIfZero();
+            BigDecimal deltaWithholdTax = withHoldTaxTotal.minus(this.totalWithholdTax).getAmountDefaultedToNullIfZero();
+            BigDecimal deltaOverdraftInterestDerived = overdraftInterestTotal.minus(this.totalOverdraftInterestDerived)
+                    .getAmountDefaultedToNullIfZero();
             this.totalInterestPosted = interestTotal.getAmountDefaultedToNullIfZero();
             this.totalWithholdTax = withHoldTaxTotal.getAmountDefaultedToNullIfZero();
             this.totalOverdraftInterestDerived = overdraftInterestTotal.getAmountDefaultedToNullIfZero();
-            this.accountBalance = Money.of(currency, this.accountBalance).plus(this.totalInterestPosted).minus(this.totalWithholdTax)
-                    .minus(this.totalOverdraftInterestDerived).getAmount();
+            this.accountBalance = getRunningBalanceOnPivotDate();
+            this.accountBalance = Money.of(currency, this.accountBalance).plus(Money.of(currency, this.totalDeposits)).plus(deltaInterest)
+                    .minus(this.totalWithdrawals).minus(deltaWithholdTax).minus(deltaOverdraftInterestDerived).getAmount();
         }
     }
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java
index fa234fcaa..e45626574 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java
@@ -48,6 +48,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdraw
 import com.google.gson.JsonElement;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
@@ -74,6 +75,7 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
 import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
 import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.slf4j.Logger;
@@ -381,12 +383,25 @@ public class SavingsAccountAssembler {
     public SavingsAccountData assembleSavings(final SavingsAccountData account) {
 
         // Update last running balance on account level
-        if (account.getTransactions() != null && account.getTransactions().size() != 0
-                && account.getSummary().getInterestPostedTillDate() != null) {
-            account.getSummary().setRunningBalanceOnPivotDate(account.getTransactions().get(account.getTransactions().size() - 1)
-                    .getRunningBalance(account.getCurrency()).getAmount());
+        final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig();
+        if (backdatedTxnsAllowedTill && account.getTransactions() != null && account.getSummary().getInterestPostedTillDate() != null) {
+            List<SavingsAccountTransactionData> removalList = new ArrayList<>();
+
+            for (int i = 0; i < account.getTransactions().size(); i++) {
+                SavingsAccountTransactionData savingsAccountTransaction = account.getTransactions().get(i);
+                removalList.add(savingsAccountTransaction);
+                if ((savingsAccountTransaction.isInterestPostingAndNotReversed()
+                        || savingsAccountTransaction.isOverdraftInterestAndNotReversed())
+                        && !savingsAccountTransaction.isReversalTransaction()) {
+                    account.getSummary().setRunningBalanceOnPivotDate(savingsAccountTransaction.getRunningBalance());
+                    account.setLastSavingsAccountTransaction(savingsAccountTransaction);
+                    break;
+                }
+            }
+            account.getTransactions().removeAll(removalList);
+        } else {
+            account.getSummary().setRunningBalanceOnPivotDate(BigDecimal.ZERO);
         }
-
         account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
         return account;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
index dc2a87309..0ee01f2ba 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
@@ -364,8 +364,12 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
         }
         final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>();
         listOfTransactionsSorted.addAll(savingsAccountData.getTransactions());
-        final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator();
-        Collections.sort(listOfTransactionsSorted, transactionComparator);
+        if (!listOfTransactionsSorted.isEmpty()) {
+            final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator();
+            Collections.sort(listOfTransactionsSorted, transactionComparator);
+        } else {
+            listOfTransactionsSorted.add(savingsAccountData.getLastSavingsAccountTransaction());
+        }
         return listOfTransactionsSorted.get(0);
     }
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
index b53a1bfed..31e8e3f0e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
@@ -272,7 +272,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
                     + "where (CASE WHEN sa.interest_posted_till_date is not null THEN tr.transaction_date >= sa.interest_posted_till_date ELSE tr.transaction_date >= sa.activatedon_date END) ";
         }
 
-        sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date < ? ) ";
+        sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date <= ? ) ";
         sql = sql + " order by sa.id, tr.transaction_date, tr.created_date, tr.id";
 
         List<SavingsAccountData> savingsAccountDataList = this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting, // NOSONAR
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java
index 018fc5b98..5234e5932 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java
@@ -76,7 +76,7 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
         Long maxSavingsIdInList = 0L;
         // initialise the executor service with fetched configurations
         final ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
-        final boolean backdatedTxnsAllowedTill = false;
+        final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig();
 
         long start = System.currentTimeMillis();
 
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 a98a796a2..3e778bba3 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
@@ -2972,18 +2972,20 @@ public class ClientSavingsIntegrationTest {
 
         configurationForBackdatedTransaction();
 
-        String transactionDate = "1 July 2022";
+        LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(10);
+        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy");
+        String startDate = formatter.format(transactionDate);
 
-        final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, transactionDate);
+        final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate);
         Assertions.assertNotNull(clientID);
 
-        final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, transactionDate);
-        this.savingsAccountHelper.depositToSavingsAccount(savingsId, "200", transactionDate, CommonConstants.RESPONSE_RESOURCE_ID);
+        final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, startDate);
+        this.savingsAccountHelper.depositToSavingsAccount(savingsId, "200", startDate, CommonConstants.RESPONSE_RESOURCE_ID);
         final String jobName = "Post Interest For Savings";
         this.scheduleJobHelper.executeAndAwaitJob(jobName);
         final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
         final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse);
-        List<HashMap> error = (List<HashMap>) validationErrorHelper.depositToSavingsAccount(savingsId, "3000", transactionDate,
+        List<HashMap> error = (List<HashMap>) validationErrorHelper.depositToSavingsAccount(savingsId, "300", startDate,
                 CommonConstants.RESPONSE_ERROR);
 
         assertEquals("error.msg.savings.transaction.is.not.allowed", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));