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 4f2400ca3 FINERACT-2011: Savings Account - transaction order is 
incorrectly mixed after certain activities
4f2400ca3 is described below

commit 4f2400ca3e63f58901fb68cebfae80a86a590c4b
Author: jmarta <[email protected]>
AuthorDate: Fri Nov 17 01:46:58 2023 +0100

    FINERACT-2011: Savings Account - transaction order is incorrectly mixed 
after certain activities
---
 .../infrastructure/core/api/JsonCommand.java       |  60 +-----
 .../domain/GlobalConfigurationProperty.java        |   5 +-
 .../portfolio/savings/domain/SavingsAccount.java   | 182 ++++++++--------
 ...countWritePlatformServiceJpaRepositoryImpl.java |   5 +-
 .../ClientSavingsIntegrationTest.java              |  50 -----
 .../SavingsAccountRecalculateBalanceTest.java      | 230 +++++++++++++++++++++
 .../common/savings/SavingsAccountHelper.java       |  34 ++-
 .../savings/SavingsApplicationTestBuilder.java     |   6 +-
 8 files changed, 361 insertions(+), 211 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
index 07c6f0023..9d560ad83 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java
@@ -32,11 +32,13 @@ import java.time.temporal.TemporalAccessor;
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import 
org.apache.fineract.infrastructure.security.domain.BasicPasswordEncodablePlatformUser;
 import org.apache.fineract.infrastructure.security.domain.PlatformUser;
 import 
org.apache.fineract.infrastructure.security.service.PlatformPasswordEncoder;
@@ -268,27 +270,11 @@ public final class JsonCommand {
     }
 
     private boolean differenceExists(final TemporalAccessor baseValue, final 
TemporalAccessor workingCopyValue) {
-        boolean differenceExists = false;
-
-        if (baseValue != null) {
-            differenceExists = !baseValue.equals(workingCopyValue);
-        } else {
-            differenceExists = workingCopyValue != null;
-        }
-
-        return differenceExists;
+        return !Objects.equals(baseValue, workingCopyValue);
     }
 
     private boolean differenceExists(final String baseValue, final String 
workingCopyValue) {
-        boolean differenceExists = false;
-
-        if (StringUtils.isNotBlank(baseValue)) {
-            differenceExists = !baseValue.equals(workingCopyValue);
-        } else {
-            differenceExists = StringUtils.isNotBlank(workingCopyValue);
-        }
-
-        return differenceExists;
+        return !Objects.equals(baseValue, workingCopyValue);
     }
 
     private boolean differenceExists(final String[] baseValue, final String[] 
workingCopyValue) {
@@ -298,47 +284,15 @@ public final class JsonCommand {
     }
 
     private boolean differenceExists(final Number baseValue, final Number 
workingCopyValue) {
-        boolean differenceExists = false;
-
-        if (baseValue != null) {
-            if (workingCopyValue != null) {
-                differenceExists = !baseValue.equals(workingCopyValue);
-            } else {
-                differenceExists = true;
-            }
-        } else {
-            differenceExists = workingCopyValue != null;
-        }
-
-        return differenceExists;
+        return !Objects.equals(baseValue, workingCopyValue);
     }
 
     private boolean differenceExists(final BigDecimal baseValue, final 
BigDecimal workingCopyValue) {
-        boolean differenceExists = false;
-
-        if (baseValue != null) {
-            if (workingCopyValue != null) {
-                differenceExists = baseValue.compareTo(workingCopyValue) != 0;
-            } else {
-                differenceExists = true;
-            }
-        } else {
-            differenceExists = workingCopyValue != null;
-        }
-
-        return differenceExists;
+        return !MathUtil.isEqualTo(baseValue, workingCopyValue);
     }
 
     private boolean differenceExists(final Boolean baseValue, final Boolean 
workingCopyValue) {
-        boolean differenceExists = false;
-
-        if (baseValue != null) {
-            differenceExists = !baseValue.equals(workingCopyValue);
-        } else {
-            differenceExists = workingCopyValue != null;
-        }
-
-        return differenceExists;
+        return !Objects.equals(baseValue, workingCopyValue);
     }
 
     public boolean parameterExists(final String parameterName) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java
index 6b884573a..a38401d24 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java
@@ -64,10 +64,9 @@ public class GlobalConfigurationProperty extends 
AbstractPersistableCustom {
     private boolean isTrapDoor;
 
     public Map<String, Object> update(final JsonCommand command) {
-
         final Map<String, Object> actualChanges = new LinkedHashMap<>(7);
 
-        if (this.isTrapDoor == true) {
+        if (this.isTrapDoor) {
             throw new GlobalConfigurationPropertyCannotBeModfied(this.getId());
         }
 
@@ -109,7 +108,6 @@ public class GlobalConfigurationProperty extends 
AbstractPersistableCustom {
         }
 
         return actualChanges;
-
     }
 
     public static GlobalConfigurationProperty newSurveyConfiguration(final 
String name) {
@@ -120,6 +118,5 @@ public class GlobalConfigurationProperty extends 
AbstractPersistableCustom {
         return new 
GlobalConfigurationPropertyData().setName(getName()).setEnabled(isEnabled()).setValue(getValue())
                 
.setDateValue(getDateValue()).setStringValue(getStringValue()).setId(this.getId()).setDescription(this.description)
                 .setTrapDoor(this.isTrapDoor);
-
     }
 }
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 5abf3857e..61a067910 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
@@ -83,6 +83,7 @@ import 
org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import 
org.apache.fineract.infrastructure.security.service.RandomPasswordGenerator;
 import org.apache.fineract.interoperation.domain.InteropIdentifier;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -510,6 +511,9 @@ public class SavingsAccount extends 
AbstractAuditableWithUTCDateTimeCustom {
         final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, 
interestPostingUpToDate, isInterestTransfer,
                 isSavingsInterestPostingAtCurrentPeriodEnd, 
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill,
                 postReversals);
+        if (postingPeriods.isEmpty()) {
+            return;
+        }
 
         Money interestPostedToDate = Money.zero(this.currency);
 
@@ -811,108 +815,102 @@ public class SavingsAccount extends 
AbstractAuditableWithUTCDateTimeCustom {
             boolean isInterestTransfer, final boolean 
isSavingsInterestPostingAtCurrentPeriodEnd, final Integer 
financialYearBeginningMonth,
             final LocalDate postInterestOnDate, final boolean 
backdatedTxnsAllowedTill, final boolean postReversals) {
 
-        // no openingBalance concept supported yet but probably will to allow
-        // for migrations.
-        Money openingAccountBalance = null;
-
+        // no openingBalance concept supported yet but probably will to allow 
for migrations.
         // Check global configurations and 'pivot' date is null
-        if (backdatedTxnsAllowedTill) {
-            openingAccountBalance = Money.of(this.currency, 
this.summary.getRunningBalanceOnPivotDate());
-        } else {
-            openingAccountBalance = Money.zero(this.currency);
-        }
-
-        // update existing transactions so derived balance fields are
-        // correct.
+        Money openingAccountBalance = backdatedTxnsAllowedTill ? 
Money.of(this.currency, this.summary.getRunningBalanceOnPivotDate())
+                : Money.zero(this.currency);
 
+        // update existing transactions so derived balance fields are correct.
         recalculateDailyBalances(openingAccountBalance, 
upToInterestCalculationDate, backdatedTxnsAllowedTill, postReversals);
 
-        // 1. default to calculate interest based on entire history OR
-        // 2. determine latest 'posting period' and find interest credited to
-        // that period
-
-        // A generate list of EndOfDayBalances (not including interest 
postings)
-        final SavingsPostingInterestPeriodType postingPeriodType = 
SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType);
-
-        final SavingsCompoundingInterestPeriodType compoundingPeriodType = 
SavingsCompoundingInterestPeriodType
-                .fromInt(this.interestCompoundingPeriodType);
+        final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
+        if (hasInterestCalculation() || hasOverdraftInterestCalculation()) {
+            // 1. default to calculate interest based on entire history OR
+            // 2. determine latest 'posting period' and find interest credited 
to that period
 
-        final SavingsInterestCalculationDaysInYearType daysInYearType = 
SavingsInterestCalculationDaysInYearType
-                .fromInt(this.interestCalculationDaysInYearType);
-        List<LocalDate> postedAsOnDates = null;
-        if (backdatedTxnsAllowedTill) {
-            postedAsOnDates = getManualPostingDatesWithPivotConfig();
-        } else {
-            postedAsOnDates = getManualPostingDates();
-        }
-        if (postInterestOnDate != null) {
-            postedAsOnDates.add(postInterestOnDate);
-        }
-        final List<LocalDateInterval> postingPeriodIntervals = 
this.savingsHelper.determineInterestPostingPeriods(
-                getStartInterestCalculationDate(), 
upToInterestCalculationDate, postingPeriodType, financialYearBeginningMonth,
-                postedAsOnDates);
+            // A generate list of EndOfDayBalances (not including interest 
postings)
+            final SavingsPostingInterestPeriodType postingPeriodType = 
SavingsPostingInterestPeriodType
+                    .fromInt(this.interestPostingPeriodType);
 
-        final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
+            final SavingsCompoundingInterestPeriodType compoundingPeriodType = 
SavingsCompoundingInterestPeriodType
+                    .fromInt(this.interestCompoundingPeriodType);
 
-        Money periodStartingBalance;
-        if (this.startInterestCalculationDate != null && 
!this.getStartInterestCalculationDate().equals(this.getActivationDate())) {
-            LocalDate startInterestCalculationDate = 
this.startInterestCalculationDate;
-            SavingsAccountTransaction transaction = null;
+            final SavingsInterestCalculationDaysInYearType daysInYearType = 
SavingsInterestCalculationDaysInYearType
+                    .fromInt(this.interestCalculationDaysInYearType);
+            List<LocalDate> postedAsOnDates = null;
             if (backdatedTxnsAllowedTill) {
-                transaction = 
findLastFilteredTransactionWithPivotConfig(startInterestCalculationDate);
+                postedAsOnDates = getManualPostingDatesWithPivotConfig();
             } else {
-                transaction = 
findLastTransaction(startInterestCalculationDate);
+                postedAsOnDates = getManualPostingDates();
+            }
+            if (postInterestOnDate != null) {
+                postedAsOnDates.add(postInterestOnDate);
             }
+            final List<LocalDateInterval> postingPeriodIntervals = 
this.savingsHelper.determineInterestPostingPeriods(
+                    getStartInterestCalculationDate(), 
upToInterestCalculationDate, postingPeriodType, financialYearBeginningMonth,
+                    postedAsOnDates);
 
-            if (transaction == null) {
-                periodStartingBalance = Money.zero(this.currency);
+            Money periodStartingBalance;
+            if (this.startInterestCalculationDate != null && 
!this.getStartInterestCalculationDate().equals(this.getActivationDate())) {
+                LocalDate startInterestCalculationDate = 
this.startInterestCalculationDate;
+                SavingsAccountTransaction transaction = null;
+                if (backdatedTxnsAllowedTill) {
+                    transaction = 
findLastFilteredTransactionWithPivotConfig(startInterestCalculationDate);
+                } else {
+                    transaction = 
findLastTransaction(startInterestCalculationDate);
+                }
+
+                if (transaction == null) {
+                    periodStartingBalance = Money.zero(this.currency);
+                } else {
+                    periodStartingBalance = Money.of(this.currency, 
this.summary.getRunningBalanceOnPivotDate());
+                }
             } else {
-                periodStartingBalance = Money.of(this.currency, 
this.summary.getRunningBalanceOnPivotDate());
+                periodStartingBalance = Money.zero(this.currency);
             }
-        } else {
-            periodStartingBalance = Money.zero(this.currency);
-        }
 
-        final SavingsInterestCalculationType interestCalculationType = 
SavingsInterestCalculationType.fromInt(this.interestCalculationType);
-        final BigDecimal interestRateAsFraction = 
getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate);
-        final BigDecimal overdraftInterestRateAsFraction = 
getEffectiveOverdraftInterestRateAsFraction(mc);
-        final Collection<Long> interestPostTransactions = 
this.savingsHelper.fetchPostInterestTransactionIds(getId());
-        final Money minBalanceForInterestCalculation = Money.of(getCurrency(), 
minBalanceForInterestCalculation());
-        final Money minOverdraftForInterestCalculation = 
Money.of(getCurrency(), this.minOverdraftForInterestCalculation);
+            final SavingsInterestCalculationType interestCalculationType = 
SavingsInterestCalculationType
+                    .fromInt(this.interestCalculationType);
+            final BigDecimal interestRateAsFraction = 
getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate);
+            final BigDecimal overdraftInterestRateAsFraction = 
getEffectiveOverdraftInterestRateAsFraction(mc);
+            final Collection<Long> interestPostTransactions = 
this.savingsHelper.fetchPostInterestTransactionIds(getId());
+            final Money minBalanceForInterestCalculation = 
Money.of(getCurrency(), minBalanceForInterestCalculation());
+            final Money minOverdraftForInterestCalculation = 
Money.of(getCurrency(), this.minOverdraftForInterestCalculation);
 
-        for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
+            for (final LocalDateInterval periodInterval : 
postingPeriodIntervals) {
 
-            boolean isUserPosting = false;
-            if 
(postedAsOnDates.contains(periodInterval.endDate().plusDays(1))) {
-                isUserPosting = true;
-            }
+                boolean isUserPosting = false;
+                if 
(postedAsOnDates.contains(periodInterval.endDate().plusDays(1))) {
+                    isUserPosting = true;
+                }
 
-            PostingPeriod postingPeriod = null;
-            List<SavingsAccountTransaction> 
orderedNonInterestPostingTransactions = null;
-            if (backdatedTxnsAllowedTill) {
-                orderedNonInterestPostingTransactions = 
retreiveOrderedNonInterestPostingSavingsTransactionsWithPivotConfig();
-            } else {
-                orderedNonInterestPostingTransactions = 
retreiveOrderedNonInterestPostingTransactions();
-            }
+                PostingPeriod postingPeriod = null;
+                List<SavingsAccountTransaction> 
orderedNonInterestPostingTransactions = null;
+                if (backdatedTxnsAllowedTill) {
+                    orderedNonInterestPostingTransactions = 
retreiveOrderedNonInterestPostingSavingsTransactionsWithPivotConfig();
+                } else {
+                    orderedNonInterestPostingTransactions = 
retreiveOrderedNonInterestPostingTransactions();
+                }
 
-            List<SavingsAccountTransactionDetailsForPostingPeriod> 
savingsAccountTransactionDetailsForPostingPeriod = 
toSavingsAccountTransactionDetailsForPostingPeriodList(
-                    orderedNonInterestPostingTransactions);
+                List<SavingsAccountTransactionDetailsForPostingPeriod> 
savingsAccountTransactionDetailsForPostingPeriod = 
toSavingsAccountTransactionDetailsForPostingPeriodList(
+                        orderedNonInterestPostingTransactions);
 
-            postingPeriod = PostingPeriod.createFrom(periodInterval, 
periodStartingBalance,
-                    savingsAccountTransactionDetailsForPostingPeriod, 
this.currency, compoundingPeriodType, interestCalculationType,
-                    interestRateAsFraction, daysInYearType.getValue(), 
upToInterestCalculationDate, interestPostTransactions,
-                    isInterestTransfer, minBalanceForInterestCalculation, 
isSavingsInterestPostingAtCurrentPeriodEnd,
-                    overdraftInterestRateAsFraction, 
minOverdraftForInterestCalculation, isUserPosting, financialYearBeginningMonth);
+                postingPeriod = PostingPeriod.createFrom(periodInterval, 
periodStartingBalance,
+                        savingsAccountTransactionDetailsForPostingPeriod, 
this.currency, compoundingPeriodType, interestCalculationType,
+                        interestRateAsFraction, daysInYearType.getValue(), 
upToInterestCalculationDate, interestPostTransactions,
+                        isInterestTransfer, minBalanceForInterestCalculation, 
isSavingsInterestPostingAtCurrentPeriodEnd,
+                        overdraftInterestRateAsFraction, 
minOverdraftForInterestCalculation, isUserPosting, financialYearBeginningMonth);
 
-            periodStartingBalance = postingPeriod.closingBalance();
+                periodStartingBalance = postingPeriod.closingBalance();
 
-            allPostingPeriods.add(postingPeriod);
-        }
+                allPostingPeriods.add(postingPeriod);
+            }
 
-        
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, 
allPostingPeriods, getLockedInUntilDate(),
-                isTransferInterestToOtherAccount());
+            
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, 
allPostingPeriods, getLockedInUntilDate(),
+                    isTransferInterestToOtherAccount());
 
-        this.summary.updateFromInterestPeriodSummaries(this.currency, 
allPostingPeriods);
+            this.summary.updateFromInterestPeriodSummaries(this.currency, 
allPostingPeriods);
+        }
 
         if (backdatedTxnsAllowedTill) {
             this.summary.updateSummaryWithPivotConfig(this.currency, 
this.savingsAccountTransactionSummaryWrapper, null,
@@ -933,6 +931,14 @@ public class SavingsAccount extends 
AbstractAuditableWithUTCDateTimeCustom {
         return this.nominalAnnualInterestRate.divide(BigDecimal.valueOf(100L), 
mc);
     }
 
+    private boolean hasInterestCalculation() {
+        return !MathUtil.isEmpty(nominalAnnualInterestRate);
+    }
+
+    private boolean hasOverdraftInterestCalculation() {
+        return isAllowOverdraft() && !MathUtil.isEmpty(getOverdraftLimit()) && 
!MathUtil.isEmpty(nominalAnnualInterestRateOverdraft);
+    }
+
     protected List<SavingsAccountTransaction> 
retreiveOrderedNonInterestPostingTransactions() {
         final List<SavingsAccountTransaction> listOfTransactionsSorted = 
retrieveListOfTransactions();
 
@@ -982,7 +988,8 @@ public class SavingsAccount extends 
AbstractAuditableWithUTCDateTimeCustom {
 
     protected void recalculateDailyBalances(final Money openingAccountBalance, 
final LocalDate interestPostingUpToDate,
             final boolean backdatedTxnsAllowedTill, boolean postReversals) {
-        Money runningBalance = openingAccountBalance.copy();
+        Money runningBalance = openingAccountBalance;
+        boolean calculateInterest = hasInterestCalculation() || 
hasOverdraftInterestCalculation();
 
         List<SavingsAccountTransaction> accountTransactionsSorted = null;
 
@@ -1017,15 +1024,14 @@ public class SavingsAccount extends 
AbstractAuditableWithUTCDateTimeCustom {
                 }
 
                 runningBalance = runningBalance.plus(transactionAmount);
-                if 
(!transaction.getRunningBalance(this.currency).isEqualTo(transactionAmount)) {
-                    transaction.setRunningBalance(runningBalance);
-                }
-                if (overdraftAmount.isZero() && 
runningBalance.isLessThanZero() && !transaction.isAmountOnHold()) {
-                    overdraftAmount = 
overdraftAmount.plus(runningBalance.getAmount().negate());
+                transaction.setRunningBalance(runningBalance);
+
+                if (MathUtil.isEmpty(overdraftAmount) && 
runningBalance.isLessThanZero() && !transaction.isAmountOnHold()) {
+                    overdraftAmount = runningBalance.negated();
                 }
-                if (transaction.getId() == null && 
overdraftAmount.isGreaterThanZero()) {
+                if (!calculateInterest || transaction.getId() == null) {
                     transaction.setOverdraftAmount(overdraftAmount);
-                } else if 
(overdraftAmount.isNotEqualTo(transaction.getOverdraftAmount(this.currency))) {
+                } else if (!MathUtil.isEqualTo(overdraftAmount, 
transaction.getOverdraftAmount(this.currency))) {
                     SavingsAccountTransaction accountTransaction = 
SavingsAccountTransaction.copyTransaction(transaction);
                     if (transaction.isChargeTransaction()) {
                         Set<SavingsAccountChargePaidBy> chargesPaidBy = 
transaction.getSavingsAccountChargesPaid();
@@ -1039,7 +1045,7 @@ public class SavingsAccount extends 
AbstractAuditableWithUTCDateTimeCustom {
                     if (postReversals) {
                         reversal = 
SavingsAccountTransaction.reversal(transaction);
                     }
-                    if (overdraftAmount.isGreaterThanZero()) {
+                    if (MathUtil.isGreaterThanZero(overdraftAmount)) {
                         accountTransaction.setOverdraftAmount(overdraftAmount);
                     }
                     accountTransaction.setRunningBalance(runningBalance);
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 7f126fe38..ca47a7f62 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
@@ -63,6 +63,7 @@ import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
 import 
org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
 import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
 import 
org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
@@ -579,8 +580,8 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                 .isSavingsInterestPostingAtCurrentPeriodEnd();
         final Integer financialYearBeginningMonth = 
this.configurationDomainService.retrieveFinancialYearBeginningMonth();
 
-        if 
(savingsAccountData.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) > 
0 || (savingsAccountData.isAllowOverdraft()
-                && 
savingsAccountData.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO)
 > 0)) {
+        if 
(MathUtil.isGreaterThanZero(savingsAccountData.getNominalAnnualInterestRate()) 
|| (savingsAccountData.isAllowOverdraft()
+                && 
MathUtil.isGreaterThanZero(savingsAccountData.getNominalAnnualInterestRateOverdraft())))
 {
             final Set<Long> existingTransactionIds = new HashSet<>();
             final Set<Long> existingReversedTransactionIds = new HashSet<>();
             updateExistingTransactionsDetails(savingsAccountData, 
existingTransactionIds, existingReversedTransactionIds);
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 ab52ce6da..5f9be5a8a 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
@@ -3162,56 +3162,6 @@ public class ClientSavingsIntegrationTest {
                 error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
     }
 
-    @Test
-    public void testSavingsAccountDepositAfterNegativeHoldAmount() {
-        this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, 
this.responseSpec);
-
-        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
-        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientID);
-
-        final Integer savingsProductID = 
createSavingsProduct(this.requestSpec, this.responseSpec, "0", null, false, 
true, false);
-        Assertions.assertNotNull(savingsProductID);
-
-        final Integer savingsId = 
this.savingsAccountHelper.applyForSavingsApplication(clientID, 
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
-        this.savingsAccountHelper.approveSavings(savingsId);
-        HashMap savingsStatusHashMap = 
this.savingsAccountHelper.activateSavings(savingsId);
-        SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
-
-        float balance = 0F;
-        float transactionAmount = 100F;
-        Integer depositTransactionId = (Integer) 
this.savingsAccountHelper.depositToSavingsAccount(savingsId,
-                String.valueOf(transactionAmount), 
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
-        Assertions.assertNotNull(depositTransactionId);
-        balance = balance + transactionAmount;
-        HashMap summary = 
this.savingsAccountHelper.getSavingsSummary(savingsId);
-        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after deposit");
-        Integer withdrawalTransactionId = (Integer) 
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId,
-                String.valueOf(transactionAmount), 
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
-        Assertions.assertNotNull(withdrawalTransactionId);
-        balance = balance - transactionAmount;
-        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
-        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after withdrawal");
-
-        float holdAmount = 50F;
-        Integer holdTransactionId = (Integer) 
this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, 
String.valueOf(holdAmount),
-                false, SavingsAccountHelper.TRANSACTION_DATE, 
CommonConstants.RESPONSE_RESOURCE_ID);
-        Assertions.assertNotNull(holdTransactionId);
-        balance = balance - holdAmount;
-        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
-        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after hold amount");
-        this.savingsAccountHelper.releaseAmount(savingsId, holdTransactionId);
-        balance = balance + holdAmount;
-        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
-        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after release amount");
-
-        depositTransactionId = (Integer) 
this.savingsAccountHelper.depositToSavingsAccount(savingsId, 
String.valueOf(transactionAmount),
-                SavingsAccountHelper.TRANSACTION_DATE, 
CommonConstants.RESPONSE_RESOURCE_ID);
-        Assertions.assertNotNull(depositTransactionId);
-        balance = balance + transactionAmount;
-        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
-        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after hold-release-deposit");
-    }
-
     private Integer createSavingsAccountDailyPostingOverdraft(final Integer 
clientID, final String startDate) {
         final Integer savingsProductID = 
createSavingsProductDailyPostingOverdraft();
         Assertions.assertNotNull(savingsProductID);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountRecalculateBalanceTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountRecalculateBalanceTest.java
new file mode 100644
index 000000000..4ae61978b
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountRecalculateBalanceTest.java
@@ -0,0 +1,230 @@
+/**
+ * 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 static org.junit.jupiter.api.Assertions.assertFalse;
+
+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.math.BigDecimal;
+import java.util.HashMap;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
+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.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Client Savings Integration Test for checking Savings Application.
+ */
+@SuppressWarnings({ "rawtypes" })
+@Order(2)
+public class SavingsAccountRecalculateBalanceTest {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SavingsAccountRecalculateBalanceTest.class);
+    public static final String DEPOSIT_AMOUNT = "2000";
+    public static final String WITHDRAW_AMOUNT = "1000";
+    public static final String WITHDRAW_AMOUNT_ADJUSTED = "500";
+    public static final String MINIMUM_OPENING_BALANCE = "1000.0";
+    public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
+    public static final String DATE_FORMAT = "dd MMMM yyyy";
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private SavingsAccountHelper savingsAccountHelper;
+    private SavingsProductHelper savingsProductHelper;
+    private SchedulerJobHelper scheduleJobHelper;
+    private PaymentTypeHelper paymentTypeHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.requestSpec.header("Fineract-Platform-TenantId", "default");
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.paymentTypeHelper = new PaymentTypeHelper();
+    }
+
+    @Test
+    public void testSavingsAccountDepositAfterNegativeHoldAmount() {
+        this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, 
this.responseSpec);
+
+        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientID);
+
+        final Integer savingsProductID = 
createSavingsProduct(this.requestSpec, this.responseSpec, "0", null, false, 
true, false, null);
+        Assertions.assertNotNull(savingsProductID);
+
+        final Integer savingsId = 
this.savingsAccountHelper.applyForSavingsApplication(clientID, 
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
+        this.savingsAccountHelper.approveSavings(savingsId);
+        HashMap savingsStatusHashMap = 
this.savingsAccountHelper.activateSavings(savingsId);
+        SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+        float balance = 0F;
+        float transactionAmount = 100F;
+        Integer depositTransactionId = (Integer) 
this.savingsAccountHelper.depositToSavingsAccount(savingsId,
+                String.valueOf(transactionAmount), 
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(depositTransactionId);
+        balance = balance + transactionAmount;
+        HashMap summary = 
this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after deposit");
+        Integer withdrawalTransactionId = (Integer) 
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId,
+                String.valueOf(transactionAmount), 
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(withdrawalTransactionId);
+        balance = balance - transactionAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after withdrawal");
+
+        float holdAmount = 50F;
+        Integer holdTransactionId = (Integer) 
this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, 
String.valueOf(holdAmount),
+                false, SavingsAccountHelper.TRANSACTION_DATE, 
CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(holdTransactionId);
+        balance = balance - holdAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after hold amount");
+        this.savingsAccountHelper.releaseAmount(savingsId, holdTransactionId);
+        balance = balance + holdAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after release amount");
+
+        depositTransactionId = (Integer) 
this.savingsAccountHelper.depositToSavingsAccount(savingsId, 
String.valueOf(transactionAmount),
+                SavingsAccountHelper.TRANSACTION_DATE, 
CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(depositTransactionId);
+        balance = balance + transactionAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after hold-release-deposit");
+    }
+
+    @Test
+    public void testSavingsAccountDepositAfterNegativeHoldAmountNoInterest() {
+        this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, 
this.responseSpec);
+
+        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientID);
+
+        final Integer savingsProductID = 
createSavingsProduct(this.requestSpec, this.responseSpec, "0", null, false, 
true, false,
+                BigDecimal.ZERO);
+        Assertions.assertNotNull(savingsProductID);
+
+        final Integer savingsId = 
this.savingsAccountHelper.applyForSavingsApplication(clientID, 
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
+        this.savingsAccountHelper.approveSavings(savingsId);
+        HashMap savingsStatusHashMap = 
this.savingsAccountHelper.activateSavings(savingsId);
+        SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+        float balance = 0F;
+        float transactionAmount = 100F;
+        Integer depositTransactionId = (Integer) 
this.savingsAccountHelper.depositToSavingsAccount(savingsId,
+                String.valueOf(transactionAmount), 
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(depositTransactionId);
+        balance = balance + transactionAmount;
+        HashMap summary = 
this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after deposit");
+        HashMap depositTransaction = 
savingsAccountHelper.getTransactionDetails(savingsId, depositTransactionId);
+        assertEquals(balance, depositTransaction.get("runningBalance"), 
"Verifying Running Balance of deposit");
+        Integer withdrawalTransactionId = (Integer) 
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId,
+                String.valueOf(transactionAmount), 
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(withdrawalTransactionId);
+        balance = balance - transactionAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after withdrawal");
+        HashMap withdrawalTransaction = 
savingsAccountHelper.getTransactionDetails(savingsId, withdrawalTransactionId);
+        assertEquals(balance, withdrawalTransaction.get("runningBalance"), 
"Verifying Running Balance of withdraw");
+
+        float holdAmount = 50F;
+        Integer holdTransactionId = (Integer) 
this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, 
String.valueOf(holdAmount),
+                false, SavingsAccountHelper.TRANSACTION_DATE, 
CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(holdTransactionId);
+        balance = balance - holdAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after hold amount");
+        Integer releaseTransactionId = 
this.savingsAccountHelper.releaseAmount(savingsId, holdTransactionId);
+        Assertions.assertNotNull(releaseTransactionId);
+        balance = balance + holdAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after release amount");
+
+        depositTransactionId = (Integer) 
this.savingsAccountHelper.depositToSavingsAccount(savingsId, 
String.valueOf(transactionAmount),
+                SavingsAccountHelper.TRANSACTION_DATE, 
CommonConstants.RESPONSE_RESOURCE_ID);
+        Assertions.assertNotNull(depositTransactionId);
+        balance = balance + transactionAmount;
+        summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+        assertEquals(balance, summary.get("availableBalance"), "Verifying 
Balance after hold-release-deposit");
+        depositTransaction = 
savingsAccountHelper.getTransactionDetails(savingsId, depositTransactionId);
+        // this is a backdated transaction and so listed before the release 
transaction
+        assertEquals(balance - holdAmount, 
depositTransaction.get("runningBalance"),
+                "Verifying Running Balance of deposit negative balance");
+
+        HashMap releaseTransaction = 
savingsAccountHelper.getTransactionDetails(savingsId, releaseTransactionId);
+        assertFalse((Boolean) releaseTransaction.get("reversed"), "Verifying 
release transaction with overdraft is not reversed");
+        assertEquals(balance, releaseTransaction.get("runningBalance"), 
"Verifying Running Balance");
+    }
+
+    // LienAtProductLevel
+    private Integer createSavingsProduct(final RequestSpecification 
requestSpec, final ResponseSpecification responseSpec,
+            final String minOpenningBalance, String 
minBalanceForInterestCalculation, final boolean enforceMinRequiredBalance,
+            final boolean allowOverDraft, final boolean lienAllowed, 
BigDecimal interestRate) {
+
+        LOG.info("------------------------------CREATING NEW SAVINGS PRODUCT 
WITH LIEN---------------------------------------");
+        SavingsProductHelper savingsProductHelper = new SavingsProductHelper();
+        if (lienAllowed) {
+            final String maxAllowedLienLimit = "2000.0";
+            savingsProductHelper.withLienAllowed(maxAllowedLienLimit);
+        }
+        if (enforceMinRequiredBalance) {
+            final String minRequiredBalance = "100.0";
+            savingsProductHelper.withMinRequiredBalance(minRequiredBalance);
+            savingsProductHelper.withEnforceMinRequiredBalance("true");
+        }
+        if (allowOverDraft) {
+            final String overDraftLimit = "500.0";
+            savingsProductHelper.withOverDraft(overDraftLimit);
+        }
+        if (interestRate != null) {
+            savingsProductHelper.withNominalAnnualInterestRate(interestRate);
+        }
+        final String savingsProductJSON = 
savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
+                
.withInterestPostingPeriodTypeAsMonthly().withInterestCalculationPeriodTypeAsDailyBalance()
+                
.withMinBalanceForInterestCalculation(minBalanceForInterestCalculation).withMinimumOpenningBalance(minOpenningBalance)
+                .build();
+
+        return SavingsProductHelper.createSavingsProduct(savingsProductJSON, 
requestSpec, responseSpec);
+    }
+
+    @AfterEach
+    public void tearDown() {
+        
GlobalConfigurationHelper.resetAllDefaultGlobalConfigurations(this.requestSpec, 
this.responseSpec);
+        
GlobalConfigurationHelper.verifyAllDefaultGlobalConfigurations(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 0c14c2672..a0b7d9cff 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
@@ -122,30 +122,35 @@ public class SavingsAccountHelper extends IntegrationTest 
{
         return sdf.format(calendar.getTime());
     }
 
-    public Integer applyForSavingsApplication(final Integer id, final Integer 
savingsProductID, final String accountType) {
-        return applyForSavingsApplicationOnDate(id, savingsProductID, 
accountType, CREATED_DATE);
+    public Integer applyForSavingsApplication(final Integer clientOrGroupId, 
final Integer savingsProductID, final String accountType) {
+        return applyForSavingsApplicationOnDate(clientOrGroupId, 
savingsProductID, accountType, CREATED_DATE);
     }
 
-    public Integer applyForSavingsApplicationOnDate(final Integer id, final 
Integer savingsProductID, final String accountType,
+    public Integer applyForSavingsApplicationOnDate(final Integer 
clientOrGroupId, final Integer savingsProductID, final String accountType,
             final String submittedOnDate) {
-        return applyForSavingsApplicationOnDate(id, savingsProductID, 
accountType, null, false, submittedOnDate);
+        return applyForSavingsApplicationOnDate(clientOrGroupId, 
savingsProductID, accountType, null, false, submittedOnDate);
     }
 
-    public Integer applyForSavingsApplicationWithExternalId(final Integer id, 
final Integer savingsProductID, final String accountType,
-            String externalId, boolean withdrawalFeeForTransfers) {
-        return applyForSavingsApplicationOnDate(id, savingsProductID, 
accountType, externalId, withdrawalFeeForTransfers, CREATED_DATE);
+    public Integer applyForSavingsApplicationWithExternalId(final Integer 
clientOrGroupId, final Integer savingsProductID,
+            final String accountType, String externalId, boolean 
withdrawalFeeForTransfers) {
+        return applyForSavingsApplicationOnDate(clientOrGroupId, 
savingsProductID, accountType, externalId, withdrawalFeeForTransfers,
+                CREATED_DATE);
     }
 
-    public Integer applyForSavingsApplicationOnDate(final Integer id, final 
Integer savingsProductID, final String accountType,
+    public Integer applyForSavingsApplicationOnDate(final Integer 
clientOrGroupId, final Integer savingsProductID, final String accountType,
             String externalId, boolean withdrawalFeeForTransfers, final String 
submittedOnDate) {
-        LOG.info("--------------------------------APPLYING FOR SAVINGS 
APPLICATION--------------------------------");
         final String savingsApplicationJSON = new 
SavingsApplicationTestBuilder() //
                 .withExternalId(externalId) //
                 .withWithdrawalFeeForTransfers(withdrawalFeeForTransfers) //
                 .withSubmittedOnDate(submittedOnDate) //
-                .build(id.toString(), savingsProductID.toString(), 
accountType);
+                .build(clientOrGroupId.toString(), 
savingsProductID.toString(), accountType);
+        return applyForSavingsApplicationOnDate(savingsApplicationJSON);
+    }
+
+    public Integer applyForSavingsApplicationOnDate(String 
savingsApplicationJson) {
+        LOG.info("--------------------------------APPLYING FOR SAVINGS 
APPLICATION--------------------------------");
         return Utils.performServerPost(this.requestSpec, this.responseSpec, 
SAVINGS_ACCOUNT_URL + "?" + Utils.TENANT_IDENTIFIER,
-                savingsApplicationJSON, "savingsId");
+                savingsApplicationJson, "savingsId");
     }
 
     public Integer applyForSavingsApplicationWithDatatables(final Integer id, 
final Integer savingsProductID, final String accountType,
@@ -923,4 +928,11 @@ public class SavingsAccountHelper extends IntegrationTest {
                 updateGsimJSON(clientID.toString(), groupID.toString(), 
productID.toString()), "");
     }
 
+    public HashMap getTransactionDetails(Integer savingsId, Integer 
transactionId) {
+        LOG.info("--------------------------------- GET savings transaction 
details -------------------------------");
+        final String url = "/fineract-provider/api/v1/savingsaccounts/" + 
savingsId + "/transactions/" + transactionId + "?"
+                + Utils.TENANT_IDENTIFIER;
+        return Utils.performServerGet(requestSpec, responseSpec, url, "");
+    }
+
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsApplicationTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsApplicationTestBuilder.java
index 6666da98e..3faf31929 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsApplicationTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsApplicationTestBuilder.java
@@ -43,14 +43,14 @@ public class SavingsApplicationTestBuilder {
     private List<Map<String, Object>> clientArray = null;
     private List<Map<String, Object>> savingsArray = null;
 
-    public String build(final String id, final String savingsProductId, final 
String accountType) {
+    public String build(final String clientOrGroupId, final String 
savingsProductId, final String accountType) {
 
         final HashMap<String, Object> map = new HashMap<>();
         map.put("dateFormat", "dd MMMM yyyy");
         if (accountType.equals("GROUP")) {
-            map.put("groupId", id);
+            map.put("groupId", clientOrGroupId);
         } else {
-            map.put("clientId", id);
+            map.put("clientId", clientOrGroupId);
         }
         map.put("productId", savingsProductId);
         map.put("locale", LOCALE);

Reply via email to