You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by av...@apache.org on 2022/02/10 13:58:42 UTC

[fineract] branch develop updated: FINERACT-1495-interesest-posting-performance-improvement (#2027)

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

avikg 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 1a9ab19  FINERACT-1495-interesest-posting-performance-improvement (#2027)
1a9ab19 is described below

commit 1a9ab196063e997881d094cd5d8d0c35b5ee0c8b
Author: Manoj <56...@users.noreply.github.com>
AuthorDate: Thu Feb 10 19:28:33 2022 +0530

    FINERACT-1495-interesest-posting-performance-improvement (#2027)
    
    * Convert JPA to JDBC
    
    * FINERACT-1495-interesest-posting-performance-improvement
    
    Co-authored-by: BLasan <be...@gmail.com>
---
 .../accounting/glaccount/data/GLAccountData.java   |  33 +
 .../journalentry/data/JournalEntryData.java        |  65 +-
 .../journalentry/domain/JournalEntry.java          |   8 +
 .../service/AccountingProcessorForSavings.java     |   1 -
 .../service/AccountingProcessorHelper.java         |  18 +-
 .../service/JournalEntryWritePlatformService.java  |   1 +
 ...EntryWritePlatformServiceJpaRepositoryImpl.java |   1 -
 .../domain/ConfigurationDomainServiceJpa.java      |   2 +
 .../organisation/monetary/data/CurrencyData.java   |  22 +
 .../monetary/domain/MonetaryCurrency.java          |   6 +
 .../organisation/monetary/domain/Money.java        |   9 +
 .../fineract/portfolio/client/data/ClientData.java |  66 ++
 .../portfolio/group/data/GroupGeneralData.java     |  31 +
 .../paymentdetail/data/PaymentDetailData.java      |   4 +
 .../savings/data/SavingsAccountChargeData.java     |  51 ++
 .../portfolio/savings/data/SavingsAccountData.java | 296 ++++++++-
 .../savings/data/SavingsAccountSummaryData.java    | 265 +++++++-
 .../data/SavingsAccountTransactionData.java        | 678 ++++++++++++++++++++-
 .../data/SavingsAccountTransactionEnumData.java    |  24 +
 .../portfolio/savings/data/SavingsProductData.java |  78 +++
 .../portfolio/savings/domain/SavingsAccount.java   | 111 +++-
 .../savings/domain/SavingsAccountAssembler.java    |  17 +
 .../domain/SavingsAccountChargesPaidByData.java    |  64 ++
 .../domain/SavingsAccountRepositoryWrapper.java    |   1 +
 .../savings/domain/SavingsAccountSummary.java      |  35 ++
 .../savings/domain/SavingsAccountTransaction.java  |  54 ++
 .../SavingsAccountTransactionDataComparator.java   |  44 ++
 .../SavingsAccountTransactionSummaryWrapper.java   | 113 ++++
 .../savings/domain/interest/PostingPeriod.java     |  75 +++
 .../SavingsAccountInterestPostingService.java}     |  12 +-
 .../SavingsAccountInterestPostingServiceImpl.java  | 564 +++++++++++++++++
 .../service/SavingsAccountReadPlatformService.java |   5 +
 .../SavingsAccountReadPlatformServiceImpl.java     | 539 +++++++++++++++-
 .../SavingsAccountWritePlatformService.java        |   7 +
 ...countWritePlatformServiceJpaRepositoryImpl.java |  72 ++-
 .../service/SavingsSchedularInterestPoster.java    | 267 +++++++-
 .../service/SavingsSchedularServiceImpl.java       | 125 +++-
 .../portfolio/tax/data/TaxComponentData.java       |  56 ++
 .../tax/data/TaxComponentHistoryData.java          |  27 +
 ...xGroupMappingsData.java => TaxDetailsData.java} |  44 +-
 .../fineract/portfolio/tax/data/TaxGroupData.java  |   4 +
 .../portfolio/tax/data/TaxGroupMappingsData.java   |  27 +
 .../fineract/portfolio/tax/service/TaxUtils.java   |  31 +
 .../core_db/V391__add_transaction_ref_column.sql   |  22 +
 44 files changed, 3880 insertions(+), 95 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/data/GLAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/data/GLAccountData.java
index 0859cb2..ef3d82a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/data/GLAccountData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/data/GLAccountData.java
@@ -69,6 +69,39 @@ public class GLAccountData implements Serializable {
         return new GLAccountData(name, parentId, glCode, manualEntriesAllowed, type, usage, description, tagId, rowIndex);
     }
 
+    public static GLAccountData createFrom(final Long id) {
+        return new GLAccountData(id);
+    }
+
+    private GLAccountData(final Long id) {
+
+        this.name = null;
+        this.parentId = null;
+        this.glCode = null;
+        this.manualEntriesAllowed = null;
+        this.type = null;
+        this.usage = null;
+        this.description = null;
+        this.tagId = null;
+        this.rowIndex = null;
+        this.id = id;
+        this.disabled = null;
+        this.nameDecorated = null;
+        this.organizationRunningBalance = null;
+        this.accountTypeOptions = null;
+        this.usageOptions = null;
+        this.assetHeaderAccountOptions = null;
+        this.liabilityHeaderAccountOptions = null;
+        this.equityHeaderAccountOptions = null;
+        this.incomeHeaderAccountOptions = null;
+        this.expenseHeaderAccountOptions = null;
+        this.allowedAssetsTagOptions = null;
+        this.allowedLiabilitiesTagOptions = null;
+        this.allowedEquityTagOptions = null;
+        this.allowedIncomeTagOptions = null;
+        this.allowedExpensesTagOptions = null;
+    }
+
     private GLAccountData(String name, Long parentId, String glCode, Boolean manualEntriesAllowed, EnumOptionData type,
             EnumOptionData usage, String description, CodeValueData tagId, Integer rowIndex) {
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/JournalEntryData.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/JournalEntryData.java
index 42cb2dd..3fe0179 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/JournalEntryData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/JournalEntryData.java
@@ -90,6 +90,7 @@ public class JournalEntryData {
     private String routingCode;
     private String receiptNumber;
     private String bankNumber;
+    private transient Long savingTransactionId;
 
     // for opening bal bulk import
     public JournalEntryData(Long officeId, LocalDate transactionDate, String currencyCode, List<CreditDebit> credits,
@@ -131,7 +132,6 @@ public class JournalEntryData {
         this.organizationRunningBalance = null;
         this.runningBalanceComputed = null;
         this.transactionDetails = null;
-
     }
 
     public static JournalEntryData importInstance(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId,
@@ -240,6 +240,40 @@ public class JournalEntryData {
         this.currency = currency;
     }
 
+    public JournalEntryData(final Long id, final Long officeId, final String glAccountName, final Long glAccountId,
+            final String glAccountCode, final EnumOptionData glAccountClassification, final LocalDate transactionDate,
+            final EnumOptionData entryType, final BigDecimal amount, final String transactionId, final Boolean manualEntry,
+            final EnumOptionData entityType, final Long entityId, final LocalDate createdDate, final String currencyCode,
+            final Long savingTransactionId) {
+        this.id = id;
+        this.officeId = officeId;
+        this.officeName = null;
+        this.glAccountName = glAccountName;
+        this.glAccountId = glAccountId;
+        this.glAccountCode = glAccountCode;
+        this.glAccountType = glAccountClassification;
+        this.transactionDate = transactionDate;
+        this.entryType = entryType;
+        this.amount = amount;
+        this.transactionId = transactionId;
+        this.savingTransactionId = savingTransactionId;
+        this.manualEntry = manualEntry;
+        this.entityType = entityType;
+        this.entityId = entityId;
+        this.createdByUserId = null;
+        this.createdDate = createdDate;
+        this.createdByUserName = null;
+        this.comments = null;
+        this.reversed = false;
+        this.referenceNumber = null;
+        this.officeRunningBalance = null;
+        this.organizationRunningBalance = null;
+        this.runningBalanceComputed = null;
+        this.transactionDetails = null;
+        this.currency = null;
+        this.currencyCode = currencyCode;
+    }
+
     public static JournalEntryData fromGLAccountData(final GLAccountData glAccountData) {
 
         final Long id = null;
@@ -300,4 +334,33 @@ public class JournalEntryData {
     public String getTransactionId() {
         return transactionId;
     }
+
+    public Long getSavingTransactionId() {
+        return this.savingTransactionId;
+    }
+
+    public String getCurrencyCode() {
+        return this.currencyCode;
+    }
+
+    public boolean isManualEntry() {
+        return this.manualEntry;
+    }
+
+    public EnumOptionData getEntityType() {
+        return this.entityType;
+    }
+
+    public Long getEntityId() {
+        return this.entityId;
+    }
+
+    public LocalDate getCreatedDate() {
+        return this.createdDate;
+    }
+
+    public Long getPaymentTypeId() {
+        return this.paymentTypeId;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java
index 3aab4a1..78f82ce 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java
@@ -222,4 +222,12 @@ public class JournalEntry extends AbstractAuditableCustom {
         return this.shareTransactionId;
     }
 
+    public boolean isReversed() {
+        return this.reversed;
+    }
+
+    public String getDescription() {
+        return this.description;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSavings.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSavings.java
index dbaf6e1..8816d58 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSavings.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSavings.java
@@ -23,5 +23,4 @@ import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
 public interface AccountingProcessorForSavings {
 
     void createJournalEntriesForSavings(SavingsDTO savingsDTO);
-
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java
index 026fddd..03e5247 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java
@@ -26,6 +26,7 @@ import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import javax.sql.DataSource;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.accounting.closure.domain.GLClosure;
 import org.apache.fineract.accounting.closure.domain.GLClosureRepository;
@@ -59,6 +60,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGL
 import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
 import org.apache.fineract.organisation.office.domain.Office;
 import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
@@ -75,7 +77,11 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumD
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository;
 import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionEnumData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -97,6 +103,9 @@ public class AccountingProcessorHelper {
     private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
     private final AccountTransfersReadPlatformService accountTransfersReadPlatformService;
     private final ChargeRepositoryWrapper chargeRepositoryWrapper;
+    private final JdbcTemplate jdbcTemplate;
+    private final DataSource dataSource;
+    private static final Logger LOG = LoggerFactory.getLogger(AccountingProcessorHelper.class);
 
     @Autowired
     public AccountingProcessorHelper(final JournalEntryRepository glJournalEntryRepository,
@@ -107,8 +116,9 @@ public class AccountingProcessorHelper {
             final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
             final GLAccountRepositoryWrapper accountRepositoryWrapper,
             final ClientTransactionRepositoryWrapper clientTransactionRepositoryWrapper,
-            final ChargeRepositoryWrapper chargeRepositoryWrapper) {
+            final ChargeRepositoryWrapper chargeRepositoryWrapper, final RoutingDataSource dataSource) {
         this.glJournalEntryRepository = glJournalEntryRepository;
+        this.dataSource = dataSource;
         this.accountMappingRepository = accountMappingRepository;
         this.closureRepository = closureRepository;
         this.officeRepositoryWrapper = officeRepositoryWrapper;
@@ -119,6 +129,7 @@ public class AccountingProcessorHelper {
         this.accountRepositoryWrapper = accountRepositoryWrapper;
         this.clientTransactionRepository = clientTransactionRepositoryWrapper;
         this.chargeRepositoryWrapper = chargeRepositoryWrapper;
+        this.jdbcTemplate = new JdbcTemplate(this.dataSource);
     }
 
     public LoanDTO populateLoanDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
@@ -842,7 +853,8 @@ public class AccountingProcessorHelper {
     }
 
     private void createCreditJournalEntryForSavings(final Office office, final String currencyCode, final GLAccount account,
-            final Long savingsId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
+            final Long savingsId, final String transactionId, final Date transactionDate, final BigDecimal amount)
+            throws DataAccessException {
         final boolean manualEntry = false;
         LoanTransaction loanTransaction = null;
         SavingsAccountTransaction savingsAccountTransaction = null;
@@ -858,6 +870,7 @@ public class AccountingProcessorHelper {
         final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
                 manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId,
                 null, loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
+
         this.glJournalEntryRepository.saveAndFlush(journalEntry);
     }
 
@@ -948,6 +961,7 @@ public class AccountingProcessorHelper {
         final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
                 manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null,
                 loanTransaction, savingsAccountTransaction, clientTransaction, shareTransactionId);
+
         this.glJournalEntryRepository.saveAndFlush(journalEntry);
     }
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java
index 054a134..bafdcbd 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java
@@ -46,4 +46,5 @@ public interface JournalEntryWritePlatformService {
     void createJournalEntriesForShares(Map<String, Object> accountingBridgeData);
 
     void revertShareAccountJournalEntries(ArrayList<Long> transactionId, Date transactionDate);
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
index d91e94b..3649246 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
@@ -530,7 +530,6 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
                     .determineProcessor(savingsDTO);
             accountingProcessorForSavings.createJournalEntriesForSavings(savingsDTO);
         }
-
     }
 
     @Transactional
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
index 51f76b7..6b6c595 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
@@ -31,6 +31,7 @@ import org.apache.fineract.useradministration.domain.Permission;
 import org.apache.fineract.useradministration.domain.PermissionRepository;
 import org.apache.fineract.useradministration.exception.PermissionNotFoundException;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -368,6 +369,7 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
         return property.getValue();
     }
 
+    @Cacheable(value = "configByName", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#propertyName)")
     private GlobalConfigurationPropertyData getGlobalConfigurationPropertyData(final String propertyName) {
         String identifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier();
         String key = identifier + "_" + propertyName;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/data/CurrencyData.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/data/CurrencyData.java
index 0697f2b..d4e1f82 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/data/CurrencyData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/data/CurrencyData.java
@@ -61,6 +61,16 @@ public class CurrencyData implements Serializable {
         this.displayLabel = generateDisplayLabel();
     }
 
+    public CurrencyData(final String code, final int decimalPlaces, final Integer inMultiplesOf) {
+        this.code = code;
+        this.name = null;
+        this.decimalPlaces = decimalPlaces;
+        this.inMultiplesOf = inMultiplesOf;
+        this.displaySymbol = null;
+        this.nameCode = null;
+        this.displayLabel = null;
+    }
+
     public String code() {
         return this.code;
     }
@@ -108,4 +118,16 @@ public class CurrencyData implements Serializable {
     public int hashCode() {
         return Objects.hash(code, name, decimalPlaces, inMultiplesOf, displaySymbol, nameCode, displayLabel);
     }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public int getDecimalPlaces() {
+        return this.decimalPlaces;
+    }
+
+    public Integer getInMultiplesOf() {
+        return this.inMultiplesOf;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java
index 74f84a9..d704b1e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java
@@ -20,6 +20,7 @@ package org.apache.fineract.organisation.monetary.domain;
 
 import javax.persistence.Column;
 import javax.persistence.Embeddable;
+import org.apache.fineract.organisation.monetary.data.CurrencyData;
 
 @Embeddable
 public class MonetaryCurrency {
@@ -54,6 +55,10 @@ public class MonetaryCurrency {
                 applicationCurrency.getCurrencyInMultiplesOf());
     }
 
+    public static MonetaryCurrency fromCurrencyData(final CurrencyData currencyData) {
+        return new MonetaryCurrency(currencyData.getCode(), currencyData.getDecimalPlaces(), currencyData.getInMultiplesOf());
+    }
+
     public String getCode() {
         return this.code;
     }
@@ -65,4 +70,5 @@ public class MonetaryCurrency {
     public Integer getCurrencyInMultiplesOf() {
         return this.inMultiplesOf;
     }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java
index eafa09f..652439e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java
@@ -23,6 +23,7 @@ import java.math.RoundingMode;
 import java.util.Iterator;
 import javax.persistence.Column;
 import javax.persistence.Embeddable;
+import org.apache.fineract.organisation.monetary.data.CurrencyData;
 
 @Embeddable
 public class Money implements Comparable<Money> {
@@ -67,10 +68,18 @@ public class Money implements Comparable<Money> {
                 currency.getCurrencyInMultiplesOf());
     }
 
+    public static Money of(final CurrencyData currency, final BigDecimal newAmount) {
+        return new Money(currency.getCode(), currency.getDecimalPlaces(), defaultToZeroIfNull(newAmount), currency.getInMultiplesOf());
+    }
+
     public static Money zero(final MonetaryCurrency currency) {
         return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), BigDecimal.ZERO, currency.getCurrencyInMultiplesOf());
     }
 
+    public static Money zero(final CurrencyData currency) {
+        return new Money(currency.getCode(), currency.getDecimalPlaces(), BigDecimal.ZERO, currency.getInMultiplesOf());
+    }
+
     protected Money() {
         this.currencyCode = null;
         this.currencyDigitsAfterDecimal = 0;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java
index deb3cd5..1f55f94 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java
@@ -129,6 +129,71 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
                 locale, dateFormat);
     }
 
+    public static ClientData createClientForInterestPosting(final Long id, final Long officeId) {
+        return new ClientData(id, officeId);
+    }
+
+    private ClientData(final Long clientId, final Long officeId) {
+        this.rowIndex = null;
+        this.dateFormat = null;
+        this.locale = null;
+        this.firstname = null;
+        this.lastname = null;
+        this.middlename = null;
+        this.fullname = null;
+        this.activationDate = null;
+        this.submittedOnDate = null;
+        this.active = null;
+        this.externalId = null;
+        this.officeId = officeId;
+        this.staffId = null;
+        this.legalFormId = null;
+        this.mobileNo = null;
+        this.dateOfBirth = null;
+        this.clientTypeId = null;
+        this.genderId = null;
+        this.clientClassificationId = null;
+        this.isStaff = false;
+        this.address = null;
+        this.accountNo = null;
+        this.status = null;
+        this.subStatus = null;
+        this.displayName = null;
+        this.gender = null;
+        this.clientType = null;
+        this.clientClassification = null;
+        this.officeName = null;
+        this.transferToOfficeId = null;
+        this.transferToOfficeName = null;
+        this.imageId = null;
+        this.imagePresent = null;
+        this.staffName = null;
+        this.timeline = null;
+        this.savingsProductId = null;
+        this.savingsProductName = null;
+        this.savingsAccountId = null;
+        this.legalForm = null;
+        this.groups = null;
+        this.officeOptions = null;
+        this.staffOptions = null;
+        this.narrations = null;
+        this.savingProductOptions = null;
+        this.savingAccountOptions = null;
+        this.genderOptions = null;
+        this.clientTypeOptions = null;
+        this.clientClassificationOptions = null;
+        this.clientNonPersonConstitutionOptions = null;
+        this.clientNonPersonMainBusinessLineOptions = null;
+        this.clientLegalFormOptions = null;
+        this.clientNonPersonDetails = null;
+        this.isAddressEnabled = null;
+        this.datatables = null;
+        this.familyMemberOptions = null;
+        this.emailAddress = null;
+        this.clientCollateralManagements = null;
+        this.id = clientId;
+    }
+
     public static ClientData importClientPersonInstance(Long legalFormId, Integer rowIndex, String firstname, String lastname,
             String middlename, LocalDate submittedOn, LocalDate activationDate, Boolean active, String externalId, Long officeId,
             Long staffId, String mobileNo, LocalDate dob, Long clientTypeId, Long genderId, Long clientClassificationId, Boolean isStaff,
@@ -638,4 +703,5 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
     public Boolean getIsAddressEnabled() {
         return this.isAddressEnabled;
     }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/data/GroupGeneralData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/data/GroupGeneralData.java
index 17cca88..65bed68 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/data/GroupGeneralData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/data/GroupGeneralData.java
@@ -126,6 +126,37 @@ public class GroupGeneralData implements Serializable {
         this.timeline = null;
     }
 
+    public GroupGeneralData(Long id, Long officeId) {
+        this.id = id;
+        this.accountNo = null;
+        this.name = null;
+        this.externalId = null;
+        this.status = null;
+        this.active = null;
+        this.activationDate = null;
+        this.officeId = officeId;
+        this.officeName = null;
+        this.centerId = null;
+        this.centerName = null;
+        this.staffId = null;
+        this.staffName = null;
+        this.hierarchy = null;
+        this.groupLevel = null;
+        this.clientMembers = null;
+        this.activeClientMembers = null;
+        this.groupRoles = null;
+        this.calendarsData = null;
+        this.collectionMeetingCalendar = null;
+        this.centerOptions = null;
+        this.officeOptions = null;
+        this.staffOptions = null;
+        this.clientOptions = null;
+        this.availableRoles = null;
+        this.selectedRole = null;
+        this.closureReasons = null;
+        this.timeline = null;
+    }
+
     public GroupGeneralData(Long id) {
         this.id = id;
         this.accountNo = null;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/data/PaymentDetailData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/data/PaymentDetailData.java
index 19667e6..b510a9c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/data/PaymentDetailData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/data/PaymentDetailData.java
@@ -72,4 +72,8 @@ public class PaymentDetailData implements Serializable {
     public int hashCode() {
         return Objects.hash(id, paymentType, accountNumber, checkNumber, routingCode, receiptNumber, bankNumber);
     }
+
+    public PaymentTypeData getPaymentType() {
+        return this.paymentType;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java
index 715f3f4..8eb09e9 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java
@@ -86,6 +86,8 @@ public class SavingsAccountChargeData implements Serializable {
 
     private final Collection<ChargeData> chargeOptions;
 
+    private ChargeData chargeData;
+
     public SavingsAccountChargeData(Long chargeId, BigDecimal amount, LocalDate dueDate) {
         this.chargeId = chargeId;
         this.amount = amount;
@@ -115,6 +117,35 @@ public class SavingsAccountChargeData implements Serializable {
         this.restartFrequencyEnum = null;
     }
 
+    public SavingsAccountChargeData(Long chargeId, BigDecimal amount, EnumOptionData chargeTimeType, boolean isPenalty) {
+        this.chargeId = chargeId;
+        this.amount = amount;
+        this.dueDate = null;
+        this.id = null;
+        this.accountId = null;
+        this.name = null;
+        this.chargeTimeType = chargeTimeType;
+        this.feeOnMonthDay = null;
+        this.feeInterval = null;
+        this.chargeCalculationType = null;
+        this.percentage = null;
+        this.amountPercentageAppliedTo = null;
+        this.currency = null;
+        this.amountPaid = null;
+        this.amountWaived = null;
+        this.amountWrittenOff = null;
+        this.amountOutstanding = null;
+        this.amountOrPercentage = null;
+        this.penalty = isPenalty;
+        this.isActive = null;
+        this.inactivationDate = null;
+        this.chargeOptions = null;
+        this.isFreeWithdrawal = null;
+        this.freeWithdrawalChargeFrequency = null;
+        this.restartFrequency = null;
+        this.restartFrequencyEnum = null;
+    }
+
     public static SavingsAccountChargeData template(final Collection<ChargeData> chargeOptions) {
         final Long id = null;
         final Long chargeId = null;
@@ -163,6 +194,10 @@ public class SavingsAccountChargeData implements Serializable {
                 restartFrequencyEnum, inactivationDate);
     }
 
+    public boolean isFeeCharge() {
+        return !this.penalty;
+    }
+
     private SavingsAccountChargeData(final Long id, final Long chargeId, final Long accountId, final String name,
             final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType,
             final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final CurrencyData currency, final BigDecimal amount,
@@ -223,4 +258,20 @@ public class SavingsAccountChargeData implements Serializable {
         return this.accountId;
     }
 
+    public boolean isPenaltyCharge() {
+        return this.penalty;
+    }
+
+    public ChargeData getCharge() {
+        return this.chargeData;
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public boolean isPenalty() {
+        return this.penalty;
+    }
+
 }
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 6bba6e7..e1722e9 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
@@ -21,18 +21,28 @@ package org.apache.fineract.portfolio.savings.data;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
 import org.apache.fineract.organisation.staff.data.StaffData;
 import org.apache.fineract.portfolio.charge.data.ChargeData;
+import org.apache.fineract.portfolio.client.data.ClientData;
+import org.apache.fineract.portfolio.group.data.GroupGeneralData;
+import org.apache.fineract.portfolio.savings.DepositAccountType;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionSummaryWrapper;
+import org.apache.fineract.portfolio.savings.domain.SavingsHelper;
 import org.apache.fineract.portfolio.tax.data.TaxGroupData;
 
 /**
@@ -79,7 +89,6 @@ public final class SavingsAccountData implements Serializable {
     private final Integer daysToDormancy;
     private final Integer daysToEscheat;
     private final BigDecimal savingsAmountOnHold;
-
     // associations
     private final SavingsAccountSummaryData summary;
     @SuppressWarnings("unused")
@@ -104,6 +113,7 @@ public final class SavingsAccountData implements Serializable {
     private final SavingsAccountChargeData annualFee;
     private final BigDecimal nominalAnnualInterestRateOverdraft;
     private final BigDecimal minOverdraftForInterestCalculation;
+    private transient List<SavingsAccountTransactionData> savingsAccountTransactionData = new ArrayList<>();
 
     private List<DatatableData> datatables = null;
 
@@ -112,7 +122,23 @@ public final class SavingsAccountData implements Serializable {
     private String locale;
     private String dateFormat;
     private transient Integer rowIndex;
+    private transient Date startInterestCalculationDate;
     private LocalDate submittedOnDate;
+    private transient SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper;
+    private transient SavingsHelper savingsHelper;
+
+    private transient SavingsAccountSummaryData savingsAccountSummaryData;
+    private transient Date activatedOnDate;
+    private transient LocalDate lockedInUntilDate;
+    private transient ClientData clientData;
+    private transient SavingsProductData savingsProductData;
+    private transient List<SavingsAccountTransactionData> newSavingsAccountTransactionData = new ArrayList<>();
+    private transient GroupGeneralData groupGeneralData;
+    private transient Long officeId;
+    private transient Set<Long> existingTransactionIds = new HashSet<>();
+    private transient Set<Long> existingReversedTransactionIds = new HashSet<>();
+    private transient Long glAccountIdForSavingsControl;
+    private transient Long glAccountIdForInterestOnSavings;
 
     public static SavingsAccountData importInstanceIndividual(Long clientId, Long productId, Long fieldOfficerId, LocalDate submittedOnDate,
             BigDecimal nominalAnnualInterestRate, EnumOptionData interestCompoundingPeriodTypeEnum,
@@ -207,6 +233,63 @@ public final class SavingsAccountData implements Serializable {
         }
     };
 
+    public void setNewSavingsAccountTransactionData(final SavingsAccountTransactionData savingsAccountTransactionData) {
+        this.newSavingsAccountTransactionData.add(savingsAccountTransactionData);
+    }
+
+    public List<SavingsAccountTransactionData> getNewSavingsAccountTransactionData() {
+        return this.newSavingsAccountTransactionData;
+    }
+
+    public void setSavingsAccountSummaryData(final SavingsAccountSummaryData savingsAccountSummaryData) {
+        this.savingsAccountSummaryData = savingsAccountSummaryData;
+    }
+
+    public void setSavingsProduct(final SavingsProductData savingsProductData) {
+        this.savingsProductData = savingsProductData;
+    }
+
+    public void setSavingsAccountTransactionData(final SavingsAccountTransactionData savingsAccountTransactionData) {
+        this.savingsAccountTransactionData.add(savingsAccountTransactionData);
+    }
+
+    public void setSubmittedOnDate(final LocalDate submittedOnDate) {
+        this.submittedOnDate = submittedOnDate;
+    }
+
+    public void setLockedInUntilDate(final LocalDate lockedInUntilDate) {
+        this.lockedInUntilDate = lockedInUntilDate;
+    }
+
+    public void setStartInterestCalculationDate(final Date startInterestCalculationDate) {
+        this.startInterestCalculationDate = startInterestCalculationDate;
+    }
+
+    public CurrencyData getCurrency() {
+        return this.currency;
+    }
+
+    public void setClientData(final ClientData clientData) {
+        this.clientData = clientData;
+    }
+
+    public void setGroupGeneralData(final GroupGeneralData groupGeneralData) {
+        this.groupGeneralData = groupGeneralData;
+    }
+
+    public void setUpdatedTransactions(List<SavingsAccountTransactionData> savingsAccountTransactionDataList) {
+        this.savingsAccountTransactionData = new ArrayList<>();
+        this.savingsAccountTransactionData.addAll(savingsAccountTransactionDataList);
+    }
+
+    public void setOfficeId(final Long officeId) {
+        this.officeId = officeId;
+    }
+
+    public Long getOfficeId() {
+        return this.officeId;
+    }
+
     public String getClientName() {
         return clientName;
     }
@@ -215,6 +298,22 @@ public final class SavingsAccountData implements Serializable {
         return accountNo;
     }
 
+    public BigDecimal getNominalAnnualInterestRate() {
+        return this.nominalAnnualInterestRate;
+    }
+
+    public BigDecimal getNominalAnnualInterestRateOverdraft() {
+        return this.nominalAnnualInterestRateOverdraft;
+    }
+
+    public boolean isAllowOverdraft() {
+        return this.allowOverdraft;
+    }
+
+    public TaxGroupData getTaxGroupData() {
+        return this.taxGroup;
+    }
+
     public Long getClientId() {
         return clientId;
     }
@@ -231,6 +330,169 @@ public final class SavingsAccountData implements Serializable {
         return timeline;
     }
 
+    public Long getId() {
+        return this.id;
+    }
+
+    public void updateTransactions(final SavingsAccountTransactionData savingsAccountTransactionData) {
+        this.savingsAccountTransactionData.add(savingsAccountTransactionData);
+    }
+
+    public boolean withHoldTax() {
+        return this.withHoldTax;
+    }
+
+    public DepositAccountType depositAccountType() {
+        return DepositAccountType.fromInt(this.depositType.getId().intValue());
+    }
+
+    public void setGlAccountIdForSavingsControl(final Long glAccountIdForSavingsControl) {
+        this.glAccountIdForSavingsControl = glAccountIdForSavingsControl;
+    }
+
+    public void setGlAccountIdForInterestOnSavings(final Long glAccountIdForInterestOnSavings) {
+        this.glAccountIdForInterestOnSavings = glAccountIdForInterestOnSavings;
+    }
+
+    public Long getGlAccountIdForSavingsControl() {
+        return this.glAccountIdForSavingsControl;
+    }
+
+    public Long getGlAccountIdForInterestOnSavings() {
+        return this.glAccountIdForInterestOnSavings;
+    }
+
+    public SavingsAccountSummaryData getSavingsAccountSummaryData() {
+        return this.savingsAccountSummaryData;
+    }
+
+    public List<SavingsAccountTransactionData> getSavingsAccountTransactionData() {
+        return this.savingsAccountTransactionData;
+    }
+
+    public void setHelpers(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
+            final SavingsHelper savingsHelper) {
+        this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper;
+        this.savingsHelper = savingsHelper;
+    }
+
+    public BigDecimal getMinBalanceForInterestCalculation() {
+        return this.minBalanceForInterestCalculation;
+    }
+
+    public BigDecimal getMinOverdraftForInterestCalculation() {
+        return this.minOverdraftForInterestCalculation;
+    }
+
+    public Integer getInterestPostingPeriodType() {
+        return this.interestPostingPeriodType.getId().intValue();
+    }
+
+    public Integer getDepositType() {
+        return this.depositType.getId().intValue();
+    }
+
+    public Integer getInterestCompoundingPeriodType() {
+        return this.interestCompoundingPeriodType.getId().intValue();
+    }
+
+    public Integer getInterestCalculationType() {
+        return this.interestCalculationType.getId().intValue();
+    }
+
+    public Integer getInterestCalculationDaysInYearType() {
+        return this.interestCalculationDaysInYearType.getId().intValue();
+    }
+
+    public LocalDate getLockedInUntilDate() {
+        return this.lockedInUntilDate;
+    }
+
+    public SavingsAccountTransactionData findLastTransaction(final LocalDate date) {
+
+        SavingsAccountTransactionData savingsTransaction = null;
+        List<SavingsAccountTransactionData> trans = getTransactions();
+        for (final SavingsAccountTransactionData transaction : trans) {
+            if (transaction.isNotReversed() && transaction.occursOn(date)) {
+                savingsTransaction = transaction;
+                break;
+            }
+        }
+
+        return savingsTransaction;
+    }
+
+    public List<SavingsAccountTransactionData> getTransactions() {
+        return this.savingsAccountTransactionData;
+    }
+
+    public LocalDate getStartInterestCalculationDate() {
+        LocalDate startInterestCalculationLocalDate = null;
+        if (this.startInterestCalculationDate != null) {
+            startInterestCalculationLocalDate = LocalDate.ofInstant(this.startInterestCalculationDate.toInstant(),
+                    DateUtils.getDateTimeZoneOfTenant());
+        } else {
+            startInterestCalculationLocalDate = getActivationLocalDate();
+        }
+        return startInterestCalculationLocalDate;
+    }
+
+    public LocalDate getActivationLocalDate() {
+        LocalDate activationLocalDate = null;
+        if (this.timeline.getActivatedOnDate() != null) {
+            activationLocalDate = this.timeline.getActivatedOnDate();
+        }
+        return activationLocalDate;
+    }
+
+    public Integer getLockinPeriodFrequencyType() {
+        return this.lockinPeriodFrequencyType.getId().intValue();
+    }
+
+    public Collection<Long> findCurrentTransactionIdsWithPivotDateConfig() {
+
+        final Collection<Long> ids = new ArrayList<>();
+        List<SavingsAccountTransactionData> trans = this.savingsAccountTransactionData;
+        for (final SavingsAccountTransactionData transaction : trans) {
+            ids.add(transaction.getId());
+        }
+        return ids;
+    }
+
+    public Collection<Long> findCurrentReversedTransactionIdsWithPivotDateConfig() {
+        final Collection<Long> ids = new ArrayList<>();
+        List<SavingsAccountTransactionData> trans = this.savingsAccountTransactionData;
+        // time consuming
+        for (final SavingsAccountTransactionData transaction : trans) {
+            if (transaction.isReversed()) {
+                ids.add(transaction.getId());
+            }
+        }
+        return ids;
+    }
+
+    public Long officeId() {
+        Long officeId = null;
+        if (this.clientData != null) {
+            officeId = this.clientData.officeId();
+        } else if (this.groupId != null) {
+            officeId = this.groupGeneralData.officeId();
+        }
+        return officeId;
+    }
+
+    public List<SavingsAccountTransactionData> getSavingsAccountTransactionsWithPivotConfig() {
+        return this.transactions.stream().collect(Collectors.toList());
+    }
+
+    public Boolean isAccrualBasedAccountingEnabledOnSavingsProduct() {
+        return this.savingsProductData.isAccrualBasedAccountingEnabled();
+    }
+
+    public Boolean isCashBasedAccountingEnabledOnSavingsProduct() {
+        return this.savingsProductData.isCashBasedAccountingEnabled();
+    }
+
     public static SavingsAccountData importInstanceGroup(Long groupId, Long productId, Long fieldOfficerId, LocalDate submittedOnDate,
             BigDecimal nominalAnnualInterestRate, EnumOptionData interestCompoundingPeriodTypeEnum,
             EnumOptionData interestPostingPeriodTypeEnum, EnumOptionData interestCalculationTypeEnum,
@@ -765,6 +1027,26 @@ public final class SavingsAccountData implements Serializable {
         return this.currency;
     }
 
+    public SavingsAccountTransactionSummaryWrapper getSavingsAccountTransactionSummaryWrapper() {
+        return this.savingsAccountTransactionSummaryWrapper;
+    }
+
+    public void setExistingTransactionIds(final Set<Long> existingTransactionIds) {
+        if (existingTransactionIds != null) {
+            this.existingTransactionIds.addAll(existingTransactionIds);
+        }
+    }
+
+    public void setExistingReversedTransactionIds(final Set<Long> existingReversedTransactionIds) {
+        if (existingReversedTransactionIds != null) {
+            this.existingReversedTransactionIds.addAll(existingReversedTransactionIds);
+        }
+    }
+
+    public SavingsHelper getSavingsHelper() {
+        return this.savingsHelper;
+    }
+
     @Override
     public boolean equals(final Object obj) {
 
@@ -793,4 +1075,16 @@ public final class SavingsAccountData implements Serializable {
     public void setDatatables(final List<DatatableData> datatables) {
         this.datatables = datatables;
     }
+
+    public SavingsAccountSummaryData getSummary() {
+        return this.summary;
+    }
+
+    public Set<Long> getExistingTransactionIds() {
+        return this.existingTransactionIds;
+    }
+
+    public Set<Long> getExistingReversedTransactionIds() {
+        return this.existingReversedTransactionIds;
+    }
 }
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 db2bd26..fbbe5cc 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
@@ -21,7 +21,16 @@ package org.apache.fineract.portfolio.savings.data;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.HashMap;
+import java.util.List;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionSummaryWrapper;
+import org.apache.fineract.portfolio.savings.domain.interest.PostingPeriod;
 
 /**
  * Immutable data object representing Savings Account summary information.
@@ -30,21 +39,23 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
 public class SavingsAccountSummaryData implements Serializable {
 
     private final CurrencyData currency;
-    private final BigDecimal totalDeposits;
-    private final BigDecimal totalWithdrawals;
-    private final BigDecimal totalWithdrawalFees;
-    private final BigDecimal totalAnnualFees;
-    private final BigDecimal totalInterestEarned;
-    private final BigDecimal totalInterestPosted;
-    private final BigDecimal accountBalance;
-    private final BigDecimal totalFeeCharge;
-    private final BigDecimal totalPenaltyCharge;
-    private final BigDecimal totalOverdraftInterestDerived;
-    private final BigDecimal totalWithholdTax;
-    private final BigDecimal interestNotPosted;
-    private final LocalDate lastInterestCalculationDate;
-    private final BigDecimal availableBalance;
-    private final LocalDate interestPostedTillDate;
+    private BigDecimal totalDeposits;
+    private BigDecimal totalWithdrawals;
+    private BigDecimal totalWithdrawalFees;
+    private BigDecimal totalAnnualFees;
+    private BigDecimal totalInterestEarned;
+    private BigDecimal totalInterestPosted;
+    private BigDecimal accountBalance;
+    private BigDecimal totalFeeCharge;
+    private BigDecimal totalPenaltyCharge;
+    private BigDecimal totalOverdraftInterestDerived;
+    private BigDecimal totalWithholdTax;
+    private BigDecimal interestNotPosted;
+    private LocalDate lastInterestCalculationDate;
+    private BigDecimal availableBalance;
+    private LocalDate interestPostedTillDate;
+    private LocalDate prevInterestPostedTillDate;
+    private transient BigDecimal runningBalanceOnInterestPostingTillDate = BigDecimal.ZERO;
 
     public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals,
             final BigDecimal totalWithdrawalFees, final BigDecimal totalAnnualFees, final BigDecimal totalInterestEarned,
@@ -69,4 +80,228 @@ public class SavingsAccountSummaryData implements Serializable {
         this.availableBalance = availableBalance;
         this.interestPostedTillDate = interestPostedTillDate;
     }
+
+    public void setPrevInterestPostedTillDate(LocalDate interestPostedTillDate) {
+        this.prevInterestPostedTillDate = interestPostedTillDate;
+    }
+
+    public LocalDate getPrevInterestPostedTillDate() {
+        return this.prevInterestPostedTillDate;
+    }
+
+    public LocalDate getInterestPostedTillDate() {
+        return this.interestPostedTillDate;
+    }
+
+    public BigDecimal getTotalInterestPosted() {
+        return this.totalInterestPosted;
+    }
+
+    public BigDecimal getTotalWithdrawalFees() {
+        return this.totalWithdrawalFees;
+    }
+
+    public BigDecimal getTotalInterestEarned() {
+        return this.totalInterestEarned;
+    }
+
+    public BigDecimal getTotalDeposits() {
+        return this.totalDeposits;
+    }
+
+    public BigDecimal getTotalWithdrawals() {
+        return this.totalWithdrawals;
+    }
+
+    public BigDecimal getTotalFeeCharge() {
+        return this.totalFeeCharge;
+    }
+
+    public BigDecimal getAvailableBalance() {
+        return this.availableBalance;
+    }
+
+    public BigDecimal getTotalOverdraftInterestDerived() {
+        return this.totalOverdraftInterestDerived;
+    }
+
+    public BigDecimal getTotalWithholdTax() {
+        return this.totalWithholdTax;
+    }
+
+    public BigDecimal getTotalPenaltyCharge() {
+        return this.totalPenaltyCharge;
+    }
+
+    public BigDecimal getTotalAnnualFees() {
+        return this.totalAnnualFees;
+    }
+
+    public LocalDate getLastInterestCalculationDate() {
+        return this.lastInterestCalculationDate;
+    }
+
+    public BigDecimal getRunningBalanceOnPivotDate() {
+        return this.runningBalanceOnInterestPostingTillDate;
+    }
+
+    public void updateSummaryWithPivotConfig(final CurrencyData currency, final SavingsAccountTransactionSummaryWrapper wrapper,
+            final SavingsAccountTransaction transaction, final List<SavingsAccountTransactionData> savingsAccountTransactions) {
+
+        if (transaction != null) {
+            Money transactionAmount = Money.of(currency, transaction.getAmount());
+            switch (SavingsAccountTransactionType.fromInt(transaction.getTypeOf())) {
+                case DEPOSIT:
+                    if (transaction.isDepositAndNotReversed() || transaction.isDividendPayoutAndNotReversed()) {
+                        this.totalDeposits = Money.of(currency, this.totalDeposits).plus(transactionAmount).getAmount();
+                        this.accountBalance = Money.of(currency, this.accountBalance).plus(transactionAmount).getAmount();
+                    }
+                break;
+                case WITHDRAWAL:
+                    if (transaction.isWithdrawal() && transaction.isNotReversed()) {
+                        this.totalWithdrawals = Money.of(currency, this.totalWithdrawals).plus(transactionAmount).getAmount();
+                        this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
+                    }
+                break;
+                case WITHDRAWAL_FEE:
+                    if (transaction.isWithdrawalFeeAndNotReversed() && transaction.isNotReversed()) {
+                        this.totalWithdrawalFees = Money.of(currency, this.totalWithdrawalFees).plus(transactionAmount).getAmount();
+                        this.totalFeeCharge = Money.of(currency, this.totalFeeCharge).plus(transactionAmount).getAmount();
+                        this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
+                    }
+                break;
+                case ANNUAL_FEE:
+                    if (transaction.isAnnualFeeAndNotReversed() && transaction.isNotReversed()) {
+                        this.totalAnnualFees = Money.of(currency, this.totalAnnualFees).plus(transactionAmount).getAmount();
+                        this.totalFeeCharge = Money.of(currency, this.totalFeeCharge).plus(transactionAmount).getAmount();
+                        this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
+                    }
+                break;
+                case WAIVE_CHARGES:
+                    if (transaction.isWaiveFeeChargeAndNotReversed()) {
+                        this.totalFeeCharge = Money.of(currency, this.totalFeeCharge).plus(transactionAmount.getAmount()).getAmount();
+                    } else if (transaction.isWaivePenaltyChargeAndNotReversed()) {
+                        this.totalPenaltyCharge = Money.of(currency, this.totalPenaltyCharge).plus(transactionAmount.getAmount())
+                                .getAmount();
+                    }
+                break;
+                case PAY_CHARGE:
+                    if (transaction.isFeeChargeAndNotReversed()) {
+                        this.totalFeeCharge = Money.of(currency, this.totalFeeCharge).plus(transactionAmount).getAmount();
+                    } else if (transaction.isPenaltyChargeAndNotReversed()) {
+                        this.totalPenaltyCharge = Money.of(currency, this.totalPenaltyCharge).plus(transactionAmount).getAmount();
+                    }
+                    if (transaction.isFeeChargeAndNotReversed() || transaction.isPenaltyChargeAndNotReversed()) {
+                        this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
+                    }
+                break;
+                case OVERDRAFT_INTEREST:
+                    if (transaction.isOverdraftInterestAndNotReversed()) {
+                        this.totalOverdraftInterestDerived = Money.of(currency, this.totalOverdraftInterestDerived).plus(transactionAmount)
+                                .getAmount();
+                        this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
+                    }
+                break;
+                case WITHHOLD_TAX:
+                    if (transaction.isWithHoldTaxAndNotReversed()) {
+                        this.totalWithholdTax = Money.of(currency, this.totalWithholdTax).plus(transactionAmount).getAmount();
+                        this.accountBalance = Money.of(currency, this.accountBalance).minus(transactionAmount).getAmount();
+                    }
+                break;
+                default:
+                break;
+            }
+        } else {
+            // boolean isUpdated = false;
+            Money interestTotal = Money.of(currency, this.totalInterestPosted);
+            Money withHoldTaxTotal = Money.of(currency, this.totalWithholdTax);
+
+            final HashMap<String, Money> map = updateRunningBalanceAndPivotDate(true, savingsAccountTransactions, interestTotal,
+                    withHoldTaxTotal, currency);
+            interestTotal = map.get("interestTotal");
+            withHoldTaxTotal = map.get("withHoldTax");
+            this.totalInterestPosted = interestTotal.getAmountDefaultedToNullIfZero();
+            this.totalWithholdTax = withHoldTaxTotal.getAmountDefaultedToNullIfZero();
+            this.accountBalance = Money.of(currency, this.accountBalance).plus(this.totalInterestPosted).minus(this.totalWithholdTax)
+                    .getAmount();
+        }
+    }
+
+    public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List<PostingPeriod> allPostingPeriods) {
+
+        Money totalEarned = Money.zero(currency);
+        LocalDate interestCalculationDate = DateUtils.getLocalDateOfTenant();
+        for (final PostingPeriod period : allPostingPeriods) {
+            Money interestEarned = period.interest();
+            interestEarned = interestEarned == null ? Money.zero(currency) : interestEarned;
+            totalEarned = totalEarned.plus(interestEarned);
+        }
+        this.lastInterestCalculationDate = interestCalculationDate;
+        this.totalInterestEarned = totalEarned.getAmount();
+    }
+
+    @SuppressWarnings("unchecked")
+    private HashMap<String, Money> updateRunningBalanceAndPivotDate(final boolean backdatedTxnsAllowedTill,
+            final List<SavingsAccountTransactionData> savingsAccountTransactions, Money interestTotal, Money withHoldTaxTotal,
+            CurrencyData currency) {
+        boolean isUpdated = false;
+        HashMap<String, Money> map = new HashMap<>();
+        for (int i = savingsAccountTransactions.size() - 1; i >= 0; i--) {
+            final SavingsAccountTransactionData savingsAccountTransaction = savingsAccountTransactions.get(i);
+            if (savingsAccountTransaction.isInterestPostingAndNotReversed() && savingsAccountTransaction.isNotReversed() && !isUpdated) {
+                setRunningBalanceOnPivotDate(savingsAccountTransaction.getRunningBalance(currency).getAmount());
+                setInterestPostedTillDate(savingsAccountTransaction.getTransactionDate());
+                isUpdated = true;
+                if (!backdatedTxnsAllowedTill) {
+                    break;
+                }
+            }
+            if (backdatedTxnsAllowedTill) {
+                if (savingsAccountTransaction.isInterestPostingAndNotReversed() && savingsAccountTransaction.isNotReversed()) {
+                    interestTotal = interestTotal.plus(savingsAccountTransaction.getAmount());
+                }
+
+                if (savingsAccountTransaction.isWithHoldTaxAndNotReversed()) {
+                    withHoldTaxTotal = withHoldTaxTotal.plus(savingsAccountTransaction.getAmount());
+                }
+            }
+        }
+        if (backdatedTxnsAllowedTill) {
+            map.put("interestTotal", interestTotal);
+            map.put("withHoldTax", withHoldTaxTotal);
+        }
+        return map;
+    }
+
+    public void updateSummary(final CurrencyData currency, final SavingsAccountTransactionSummaryWrapper wrapper,
+            final List<SavingsAccountTransactionData> transactions) {
+
+        this.totalDeposits = wrapper.calculateTotalDeposits(currency, transactions);
+        this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, transactions);
+        this.totalInterestPosted = wrapper.calculateTotalInterestPosted(currency, transactions);
+        this.totalWithdrawalFees = wrapper.calculateTotalWithdrawalFees(currency, transactions);
+        this.totalAnnualFees = wrapper.calculateTotalAnnualFees(currency, transactions);
+        this.totalFeeCharge = wrapper.calculateTotalFeesCharge(currency, transactions);
+        this.totalPenaltyCharge = wrapper.calculateTotalPenaltyCharge(currency, transactions);
+        this.totalFeeCharge = wrapper.calculateTotalFeesChargeWaived(currency, transactions);
+        this.totalPenaltyCharge = wrapper.calculateTotalPenaltyChargeWaived(currency, transactions);
+        this.totalOverdraftInterestDerived = wrapper.calculateTotalOverdraftInterest(currency, transactions);
+        this.totalWithholdTax = wrapper.calculateTotalWithholdTaxWithdrawal(currency, transactions);
+
+        // boolean isUpdated = false;
+        updateRunningBalanceAndPivotDate(false, transactions, null, null, currency);
+
+        this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals)
+                .minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).minus(this.totalFeeCharge).minus(this.totalPenaltyCharge)
+                .minus(totalOverdraftInterestDerived).minus(totalWithholdTax).getAmount();
+    }
+
+    public void setRunningBalanceOnPivotDate(final BigDecimal runningBalanceOnPivotDate) {
+        this.runningBalanceOnInterestPostingTillDate = runningBalanceOnPivotDate;
+    }
+
+    public void setInterestPostedTillDate(final LocalDate date) {
+        this.interestPostedTillDate = date;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
index 6fb9cc0..3ff8c06 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
@@ -21,15 +21,32 @@ package org.apache.fineract.portfolio.savings.data;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.account.data.AccountTransferData;
 import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountChargesPaidByData;
+import org.apache.fineract.portfolio.savings.domain.interest.EndOfDayBalance;
 import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
+import org.apache.fineract.portfolio.tax.data.TaxComponentData;
+import org.apache.fineract.portfolio.tax.data.TaxDetailsData;
+import org.springframework.util.CollectionUtils;
 
 /**
  * Immutable data object representing a savings account transaction.
@@ -37,7 +54,7 @@ import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
 @SuppressWarnings("unused")
 public final class SavingsAccountTransactionData implements Serializable {
 
-    private final Long id;
+    private Long id;
     private final SavingsAccountTransactionEnumData transactionType;
     private final Long accountId;
     private final String accountNo;
@@ -46,13 +63,15 @@ public final class SavingsAccountTransactionData implements Serializable {
     private final PaymentDetailData paymentDetailData;
     private final BigDecimal amount;
     private final BigDecimal outstandingChargeAmount;
-    private final BigDecimal runningBalance;
-    private final boolean reversed;
+    private BigDecimal runningBalance;
+    private boolean reversed;
     private final AccountTransferData transfer;
-    private final LocalDate submittedOnDate;
+    private Date submittedOnDate;
     private final boolean interestedPostedAsOn;
     private final String submittedByUsername;
     private final String note;
+    private final boolean isManualTransaction;
+    private Set<SavingsAccountChargesPaidByData> chargesPaidByData = new HashSet<>();
 
     // templates
     final Collection<PaymentTypeData> paymentTypeOptions;
@@ -70,6 +89,13 @@ public final class SavingsAccountTransactionData implements Serializable {
     private String routingCode;
     private String receiptNumber;
     private String bankNumber;
+    private BigDecimal cumulativeBalance;
+    private LocalDate balanceEndDate;
+    private transient List<TaxDetailsData> taxDetails = new ArrayList<>();
+    private Integer balanceNumberOfDays;
+    private BigDecimal overdraftAmount;
+    private transient Long modifiedId;
+    private transient String refNo;
 
     public static SavingsAccountTransactionData importInstance(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId,
             String accountNumber, String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId,
@@ -78,6 +104,442 @@ public final class SavingsAccountTransactionData implements Serializable {
                 receiptNumber, bankNumber, savingsAccountId, transactionType, rowIndex, locale, dateFormat);
     }
 
+    public static SavingsAccountTransactionData interestPosting(final SavingsAccountData savingsAccount, final LocalDate date,
+            final Money amount, final boolean isManualTransaction) {
+        final boolean isReversed = false;
+        final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.INTEREST_POSTING;
+        SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData(
+                savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
+                savingsAccountTransactionType.getValue().toString());
+        return new SavingsAccountTransactionData(amount.getAmount(), date, savingsAccount.getId(), savingsAccountTransactionEnumData,
+                isReversed, null, isManualTransaction);
+    }
+
+    public static SavingsAccountTransactionData overdraftInterest(final SavingsAccountData savingsAccount, final LocalDate date,
+            final Money amount, final boolean isManualTransaction) {
+        final boolean isReversed = false;
+        final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.OVERDRAFT_INTEREST;
+        SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData(
+                savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
+                savingsAccountTransactionType.getValue().toString());
+        return new SavingsAccountTransactionData(amount.getAmount(), date, savingsAccount.getId(), savingsAccountTransactionEnumData,
+                isReversed, null, isManualTransaction);
+    }
+
+    public List<TaxDetailsData> getTaxDetails() {
+        return this.taxDetails;
+    }
+
+    public boolean isInterestPostingAndNotReversed() {
+        return this.transactionType.isInterestPosting() && isNotReversed();
+    }
+
+    public void setTaxDetails(final TaxDetailsData taxDetails) {
+        this.taxDetails.add(taxDetails);
+    }
+
+    public boolean isOverdraftInterestAndNotReversed() {
+        return this.transactionType.isIncomeFromInterest() && isNotReversed();
+    }
+
+    public boolean isDebit() {
+        return isWithdrawal() || isWithdrawalFeeAndNotReversed() || isAnnualFeeAndNotReversed() || isPayCharge()
+                || isOverdraftInterestAndNotReversed() || isWithHoldTaxAndNotReversed();
+    }
+
+    public boolean isWithdrawalFeeAndNotReversed() {
+        return this.transactionType.isWithdrawalFee() && isNotReversed();
+    }
+
+    public boolean isPayCharge() {
+        return this.transactionType.isPayCharge();
+    }
+
+    public void updateRunningBalance(final Money balance) {
+        this.runningBalance = balance.getAmount();
+    }
+
+    public void updateOverdraftAmount(BigDecimal overdraftAmount) {
+        this.overdraftAmount = overdraftAmount;
+    }
+
+    public boolean isAmountOnHold() {
+        return this.transactionType.isAmountOnHold();
+    }
+
+    public boolean isAnnualFeeAndNotReversed() {
+        return isAnnualFee() && isNotReversed();
+    }
+
+    public boolean isAnnualFee() {
+        return this.transactionType.isAnnualFee();
+    }
+
+    public Money getRunningBalance(final CurrencyData currency) {
+        return Money.of(currency, this.runningBalance);
+    }
+
+    public boolean isDepositAndNotReversed() {
+        return this.transactionType.isDeposit() && isNotReversed();
+    }
+
+    public boolean isDividendPayoutAndNotReversed() {
+        return this.transactionType.isDividendPayout() && isNotReversed();
+    }
+
+    public void setRefNo(final String uuid) {
+        this.refNo = uuid;
+    }
+
+    public String getRefNo() {
+        return this.refNo;
+    }
+
+    public void setBalanceNumberOfDays(final Integer balanceNumberOfDays) {
+        this.balanceNumberOfDays = balanceNumberOfDays;
+    }
+
+    public Integer getBalanceNumberOfDays() {
+        return this.balanceNumberOfDays;
+    }
+
+    public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
+        final MonetaryCurrency currency = openingBalance.getCurrency();
+        Money endOfDayBalance = openingBalance.copy();
+        if (isDeposit() || isDividendPayoutAndNotReversed()) {
+            endOfDayBalance = openingBalance.plus(getAmount());
+        } else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
+
+            if (openingBalance.isGreaterThanZero()) {
+                endOfDayBalance = openingBalance.minus(getAmount());
+            } else {
+                endOfDayBalance = Money.of(currency, this.runningBalance);
+            }
+        }
+
+        return EndOfDayBalance.from(getTransactionLocalDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays);
+    }
+
+    public boolean isChargeTransactionAndNotReversed() {
+        return this.transactionType.isChargeTransaction() && isNotReversed();
+    }
+
+    public LocalDate getLastTransactionDate() {
+        return this.transactionDate;
+    }
+
+    public boolean occursOn(final LocalDate occursOnDate) {
+        return getTransactionLocalDate().isEqual(occursOnDate);
+    }
+
+    public LocalDate getTransactionLocalDate() {
+        return this.transactionDate;
+    }
+
+    public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) {
+
+        final MonetaryCurrency currency = openingBalance.getCurrency();
+        Money endOfDayBalance = openingBalance.copy();
+
+        int numberOfDaysOfBalance = this.balanceNumberOfDays;
+
+        LocalDate balanceStartDate = getTransactionLocalDate();
+        LocalDate balanceEndDate = getEndOfBalanceLocalDate();
+
+        if (boundedBy.startDate().isAfter(balanceStartDate)) {
+            balanceStartDate = boundedBy.startDate();
+            final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
+            numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
+        } else {
+            if (isDeposit() || isDividendPayoutAndNotReversed()) {
+                endOfDayBalance = endOfDayBalance.plus(getAmount());
+            } else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
+                if (endOfDayBalance.isGreaterThanZero()) {
+                    endOfDayBalance = endOfDayBalance.minus(getAmount());
+                } else {
+                    endOfDayBalance = Money.of(currency, this.runningBalance);
+                }
+            }
+        }
+
+        if (balanceEndDate.isAfter(boundedBy.endDate())) {
+            balanceEndDate = boundedBy.endDate();
+            final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
+            numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
+        }
+
+        return EndOfDayBalance.from(balanceStartDate, openingBalance, endOfDayBalance, numberOfDaysOfBalance);
+    }
+
+    public void reverse() {
+        this.reversed = true;
+    }
+
+    public boolean fallsWithin(final LocalDateInterval periodInterval) {
+        final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate());
+        return periodInterval.contains(balanceInterval);
+    }
+
+    public LocalDate getEndOfBalanceLocalDate() {
+        return this.balanceEndDate == null ? null : this.balanceEndDate;
+    }
+
+    public void zeroBalanceFields() {
+        this.runningBalance = null;
+        this.cumulativeBalance = null;
+        this.balanceEndDate = null;
+        this.balanceNumberOfDays = null;
+    }
+
+    public boolean isAmountRelease() {
+        return this.transactionType.isAmountRelease();
+    }
+
+    public boolean isCredit() {
+        return isDeposit() || isInterestPostingAndNotReversed() || isDividendPayoutAndNotReversed();
+    }
+
+    public boolean isDeposit() {
+        return this.transactionType.isDeposit();
+    }
+
+    public static SavingsAccountTransactionData copyTransaction(SavingsAccountTransactionData accountTransaction) {
+        return new SavingsAccountTransactionData(accountTransaction.getSavingsAccountId(), null, accountTransaction.getPaymentDetailData(),
+                accountTransaction.getTransactionType(), accountTransaction.getTransactionLocalDate(),
+                accountTransaction.getSubmittedOnDate(), accountTransaction.getAmount(), accountTransaction.isReversed(), null,
+                accountTransaction.isManualTransaction());
+    }
+
+    private SavingsAccountTransactionData(final Long savingsId, final Long officeId, final PaymentDetailData paymentDetailData,
+            final SavingsAccountTransactionEnumData savingsAccountTransactionType, final LocalDate transactionDate, final Date createdDate,
+            final BigDecimal amount, final boolean isReversed, final Long userId, final boolean isManualTransaction) {
+        this.savingsAccountId = savingsId;
+        this.paymentDetailData = paymentDetailData;
+        this.transactionType = savingsAccountTransactionType;
+        this.transactionDate = transactionDate;
+        this.submittedOnDate = createdDate;
+        this.amount = amount;
+        this.isManualTransaction = isManualTransaction;
+        this.id = null;
+        this.accountId = null;
+        this.accountNo = null;
+        this.date = null;
+        this.currency = null;
+        this.outstandingChargeAmount = null;
+        this.runningBalance = null;
+        this.reversed = isReversed;
+        this.transfer = null;
+        this.interestedPostedAsOn = false;
+        this.rowIndex = null;
+        this.dateFormat = null;
+        this.locale = null;
+        this.transactionAmount = null;
+        this.paymentTypeId = null;
+        this.accountNumber = null;
+        this.checkNumber = null;
+        this.routingCode = null;
+        this.receiptNumber = null;
+        this.bankNumber = null;
+        this.paymentTypeOptions = null;
+        this.submittedByUsername = null;
+        this.note = null;
+    }
+
+    public boolean isChargeTransaction() {
+        return this.transactionType.isChargeTransaction();
+    }
+
+    public Set<SavingsAccountChargesPaidByData> getSavingsAccountChargesPaid() {
+        return this.chargesPaidByData;
+    }
+
+    public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
+        // balance end date should not be before transaction date
+        if (endOfBalanceDate != null && endOfBalanceDate.isBefore(this.transactionDate)) {
+            this.balanceEndDate = this.transactionDate;
+        } else if (endOfBalanceDate != null) {
+            this.balanceEndDate = endOfBalanceDate;
+        } else {
+            this.balanceEndDate = null;
+        }
+        this.balanceNumberOfDays = LocalDateInterval.create(getTransactionLocalDate(), endOfBalanceDate).daysInPeriodInclusiveOfEndDate();
+        this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
+    }
+
+    public boolean hasNotAmount(final Money amountToCheck) {
+        final Money transactionAmount = getAmount(amountToCheck.getCurrency());
+        return transactionAmount.isNotEqualTo(amountToCheck);
+    }
+
+    public boolean isFeeChargeAndNotReversed() {
+        return isFeeCharge() && isNotReversed();
+    }
+
+    public boolean isFeeCharge() {
+        final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
+        return (isPayCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false;
+    }
+
+    public void setChargesPaidByData(final SavingsAccountChargesPaidByData savingsAccountChargesPaidByData) {
+        this.chargesPaidByData.add(savingsAccountChargesPaidByData);
+    }
+
+    public void setOverdraftAmount(final BigDecimal overdraftAmount) {
+        this.overdraftAmount = overdraftAmount;
+    }
+
+    public boolean isPenaltyChargeAndNotReversed() {
+        return isPenaltyCharge() && isNotReversed();
+    }
+
+    public boolean isPenaltyCharge() {
+        final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
+        return (isPayCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false;
+    }
+
+    public boolean isWaiveFeeChargeAndNotReversed() {
+        return isWaiveFeeCharge() && isNotReversed();
+    }
+
+    public boolean isWaiveFeeCharge() {
+        final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
+        return (isWaiveCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false;
+    }
+
+    public boolean isWaiveCharge() {
+        return SavingsAccountTransactionType.fromInt(this.transactionType.getId().intValue()).isWaiveCharge();
+    }
+
+    public boolean isWaivePenaltyChargeAndNotReversed() {
+        return isWaivePenaltyCharge() && isNotReversed();
+    }
+
+    public boolean isWaivePenaltyCharge() {
+        final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
+        return (isWaiveCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false;
+    }
+
+    private SavingsAccountChargesPaidByData getSavingsAccountChargePaidBy() {
+        if (!CollectionUtils.isEmpty(this.chargesPaidByData)) {
+            return this.chargesPaidByData.iterator().next();
+        }
+        return null;
+    }
+
+    public Money getAmount(final MonetaryCurrency currency) {
+        return Money.of(currency, this.amount);
+    }
+
+    public static SavingsAccountTransactionData withHoldTax(final SavingsAccountData savingsAccount, final LocalDate date,
+            final Money amount, final Map<TaxComponentData, BigDecimal> taxDetails) {
+        final boolean isReversed = false;
+        final boolean isManualTransaction = false;
+        SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.WITHHOLD_TAX;
+        SavingsAccountTransactionEnumData transactionType = new SavingsAccountTransactionEnumData(
+                savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
+                savingsAccountTransactionType.getValue().toString());
+        SavingsAccountTransactionData accountTransaction = new SavingsAccountTransactionData(amount.getAmount(), date,
+                savingsAccount.getId(), transactionType, isReversed, null, isManualTransaction);
+        updateTaxDetails(taxDetails, accountTransaction);
+        return accountTransaction;
+    }
+
+    public static void updateTaxDetails(final Map<TaxComponentData, BigDecimal> taxDetails,
+            final SavingsAccountTransactionData accountTransaction) {
+        if (taxDetails != null) {
+            for (Map.Entry<TaxComponentData, BigDecimal> mapEntry : taxDetails.entrySet()) {
+                accountTransaction.getTaxDetails().add(new TaxDetailsData(mapEntry.getKey(), mapEntry.getValue()));
+            }
+        }
+    }
+
+    public Map<String, Object> toMapData(final CurrencyData currencyData, final Long officeId) {
+        final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
+
+        final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations
+                .transactionType(this.transactionType.getId().intValue());
+
+        thisTransactionData.put("id", getId());
+        thisTransactionData.put("officeId", officeId);
+        thisTransactionData.put("type", transactionType);
+        thisTransactionData.put("reversed", Boolean.valueOf(isReversed()));
+        thisTransactionData.put("date", getTransactionLocalDate());
+        thisTransactionData.put("currency", currencyData);
+        thisTransactionData.put("amount", this.amount);
+        thisTransactionData.put("overdraftAmount", this.overdraftAmount);
+
+        if (this.paymentDetailData != null) {
+            thisTransactionData.put("paymentTypeId", this.paymentDetailData.getPaymentType().getId());
+        }
+
+        /***
+         * Sending data in a map, though in savings we currently expect a transaction to always repay a single charge
+         * (or may repay a part of a single charge too)
+         ***/
+        if (!this.chargesPaidByData.isEmpty()) {
+            final List<Map<String, Object>> savingsChargesPaidData = new ArrayList<>();
+            for (final SavingsAccountChargesPaidByData chargePaidBy : this.chargesPaidByData) {
+                final Map<String, Object> savingChargePaidData = new LinkedHashMap<>();
+                savingChargePaidData.put("chargeId", chargePaidBy.getSavingsAccountCharge());
+                savingChargePaidData.put("isPenalty", chargePaidBy.getSavingsAccountCharge().isPenalty());
+                savingChargePaidData.put("savingsChargeId", chargePaidBy.getSavingsAccountCharge().getId());
+                savingChargePaidData.put("amount", chargePaidBy.getAmount());
+
+                savingsChargesPaidData.add(savingChargePaidData);
+            }
+            thisTransactionData.put("savingsChargesPaid", savingsChargesPaidData);
+        }
+
+        if (this.taxDetails != null && !this.taxDetails.isEmpty()) {
+            final List<Map<String, Object>> taxData = new ArrayList<>();
+            for (final TaxDetailsData taxDetails : this.taxDetails) {
+                final Map<String, Object> taxDetailsData = new HashMap<>();
+                taxDetailsData.put("amount", taxDetails.getAmount());
+                if (taxDetails.getTaxComponent().getCreditAcount() != null) {
+                    taxDetailsData.put("creditAccountId", taxDetails.getTaxComponent().getCreditAcount().getId());
+                }
+                taxData.add(taxDetailsData);
+            }
+            thisTransactionData.put("taxDetails", taxData);
+        }
+
+        return thisTransactionData;
+    }
+
+    private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long savingsAccountId,
+            SavingsAccountTransactionEnumData transactionType, boolean isReversed, String locale, boolean isManualTransaction) {
+        this.id = null;
+        this.transactionType = transactionType;
+        this.accountId = null;
+        this.accountNo = null;
+        this.date = transactionDate;
+        this.currency = null;
+        this.paymentDetailData = null;
+        this.amount = transactionAmount;
+        this.outstandingChargeAmount = null;
+        this.runningBalance = null;
+        this.reversed = isReversed;
+        this.transfer = null;
+        this.submittedOnDate = Date.from(transactionDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        this.interestedPostedAsOn = false;
+        this.rowIndex = null;
+        this.savingsAccountId = savingsAccountId;
+        this.dateFormat = null;
+        this.locale = locale;
+        this.transactionDate = transactionDate;
+        this.transactionAmount = transactionAmount;
+        this.paymentTypeId = null;
+        this.accountNumber = null;
+        this.checkNumber = null;
+        this.routingCode = null;
+        this.receiptNumber = null;
+        this.bankNumber = null;
+        this.paymentTypeOptions = null;
+        this.submittedByUsername = null;
+        this.note = null;
+        this.isManualTransaction = isManualTransaction;
+    }
+
     private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId, String accountNumber,
             String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId,
             SavingsAccountTransactionEnumData transactionType, Integer rowIndex, String locale, String dateFormat) {
@@ -110,6 +572,44 @@ public final class SavingsAccountTransactionData implements Serializable {
         this.paymentTypeOptions = null;
         this.submittedByUsername = null;
         this.note = null;
+        this.isManualTransaction = false;
+    }
+
+    private SavingsAccountTransactionData(Integer id, BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId,
+            String accountNumber, String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId,
+            SavingsAccountTransactionEnumData transactionType, Integer rowIndex, String locale, String dateFormat,
+            BigDecimal cumulativeBalance) {
+        this.id = null;
+        this.transactionType = transactionType;
+        this.accountId = null;
+        this.accountNo = null;
+        this.date = null;
+        this.currency = null;
+        this.paymentDetailData = null;
+        this.amount = null;
+        this.outstandingChargeAmount = null;
+        this.runningBalance = null;
+        this.reversed = false;
+        this.submittedOnDate = null;
+        this.interestedPostedAsOn = false;
+        this.rowIndex = rowIndex;
+        this.savingsAccountId = savingsAccountId;
+        this.dateFormat = dateFormat;
+        this.locale = locale;
+        this.transactionDate = transactionDate;
+        this.transactionAmount = transactionAmount;
+        this.paymentTypeId = paymentTypeId;
+        this.accountNumber = accountNumber;
+        this.checkNumber = checkNumber;
+        this.routingCode = routingCode;
+        this.receiptNumber = receiptNumber;
+        this.bankNumber = bankNumber;
+        this.paymentTypeOptions = null;
+        this.submittedByUsername = null;
+        this.note = null;
+        this.cumulativeBalance = cumulativeBalance;
+        this.transfer = null;
+        this.isManualTransaction = false;
     }
 
     public Integer getRowIndex() {
@@ -120,10 +620,128 @@ public final class SavingsAccountTransactionData implements Serializable {
         return savingsAccountId;
     }
 
+    public PaymentDetailData getPaymentDetailData() {
+        return this.paymentDetailData;
+    }
+
+    public Long getId() {
+        return this.id;
+    }
+
     public SavingsAccountTransactionEnumData getTransactionType() {
         return transactionType;
     }
 
+    public LocalDate getTransactionDate() {
+        return this.transactionDate;
+    }
+
+    public LocalDate getDate() {
+        return this.date;
+    }
+
+    public BigDecimal getAmount() {
+        return this.amount;
+    }
+
+    public boolean isWithdrawal() {
+        return this.transactionType.isWithdrawal();
+    }
+
+    public boolean isInterestPosting() {
+        return this.transactionType.isInterestPosting() || this.transactionType.isOverDraftInterestPosting();
+    }
+
+    public boolean isManualTransaction() {
+        return this.isManualTransaction;
+    }
+
+    public boolean isReversed() {
+        return this.reversed;
+    }
+
+    public BigDecimal getRunningBalance() {
+        return this.runningBalance;
+    }
+
+    public BigDecimal getCumulativeBalance() {
+        return this.cumulativeBalance;
+    }
+
+    public LocalDate getBalanceEndDate() {
+        return this.balanceEndDate;
+    }
+
+    public Date getSubmittedOnDate() {
+        return this.submittedOnDate;
+    }
+
+    public boolean isWithHoldTaxAndNotReversed() {
+        return SavingsAccountTransactionType.fromInt(this.transactionType.getId().intValue()).isWithHoldTax() && isNotReversed();
+    }
+
+    public boolean isNotReversed() {
+        return !isReversed();
+    }
+
+    public BigDecimal getOverdraftAmount() {
+        return this.overdraftAmount;
+    }
+
+    public boolean spansAnyPortionOf(final LocalDateInterval periodInterval) {
+        final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate());
+        return balanceInterval.containsPortionOf(periodInterval);
+    }
+
+    public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
+            final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
+            final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
+            final boolean reversed, final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance,
+            final LocalDate balanceEndDate) {
+        final Collection<PaymentTypeData> paymentTypeOptions = null;
+        return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
+                amount, outstandingChargeAmount, runningBalance, reversed, submittedOnDate, paymentTypeOptions, interestedPostedAsOn,
+                cumulativeBalance, balanceEndDate);
+    }
+
+    private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
+            final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
+            final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
+            final boolean reversed, final LocalDate submittedOnDate, final Collection<PaymentTypeData> paymentTypeOptions,
+            final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance, final LocalDate balanceEndDate) {
+
+        this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, outstandingChargeAmount,
+                runningBalance, reversed, paymentTypeOptions, submittedOnDate, interestedPostedAsOn, cumulativeBalance, balanceEndDate);
+    }
+
+    private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
+            final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
+            final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
+            final boolean reversed, final Collection<PaymentTypeData> paymentTypeOptions, final LocalDate submittedOnDate,
+            final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance, final LocalDate balanceEndDate) {
+        this.id = id;
+        this.transactionDate = date;
+        this.transactionType = transactionType;
+        this.paymentDetailData = paymentDetailData;
+        this.accountId = savingsId;
+        this.accountNo = savingsAccountNo;
+        this.date = date;
+        this.currency = currency;
+        this.amount = amount;
+        this.outstandingChargeAmount = outstandingChargeAmount;
+        this.runningBalance = runningBalance;
+        this.reversed = reversed;
+        this.paymentTypeOptions = paymentTypeOptions;
+        this.submittedOnDate = Date.from(submittedOnDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        this.interestedPostedAsOn = interestedPostedAsOn;
+        this.cumulativeBalance = cumulativeBalance;
+        this.transfer = null;
+        this.submittedByUsername = null;
+        this.note = null;
+        this.balanceEndDate = balanceEndDate;
+        this.isManualTransaction = false;
+    }
+
     public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
             final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
             final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
@@ -146,6 +764,12 @@ public final class SavingsAccountTransactionData implements Serializable {
                 interestedPostedAsOn, submittedByUsername, note);
     }
 
+    public static SavingsAccountTransactionData create(final Long id) {
+        final Collection<PaymentTypeData> paymentTypeOptions = null;
+        return new SavingsAccountTransactionData(id, null, null, null, null, null, null, null, null, null, false, null, paymentTypeOptions,
+                null, false, null, null);
+    }
+
     public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo,
             final LocalDate defaultLocalDate, final CurrencyData currency) {
         final Long id = null;
@@ -182,7 +806,7 @@ public final class SavingsAccountTransactionData implements Serializable {
             final boolean interestedPostedAsOn, final String submittedByUsername, final String note) {
 
         this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, outstandingChargeAmount,
-                runningBalance, reversed, transfer, paymentTypeOptions, null, interestedPostedAsOn, submittedByUsername, note);
+                runningBalance, reversed, transfer, paymentTypeOptions, date, interestedPostedAsOn, submittedByUsername, note);
     }
 
     private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
@@ -203,10 +827,47 @@ public final class SavingsAccountTransactionData implements Serializable {
         this.reversed = reversed;
         this.transfer = transfer;
         this.paymentTypeOptions = paymentTypeOptions;
-        this.submittedOnDate = submittedOnDate;
+        if (submittedOnDate != null) {
+            this.submittedOnDate = Date.from(submittedOnDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        } else {
+            this.submittedOnDate = null;
+        }
+
         this.interestedPostedAsOn = interestedPostedAsOn;
         this.submittedByUsername = submittedByUsername;
         this.note = note;
+        this.isManualTransaction = false;
+    }
+
+    private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
+            final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
+            final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
+            final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
+            final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance) {
+        this.id = id;
+        this.transactionType = transactionType;
+        this.paymentDetailData = paymentDetailData;
+        this.accountId = savingsId;
+        this.accountNo = savingsAccountNo;
+        this.date = date;
+        this.currency = currency;
+        this.amount = amount;
+        this.outstandingChargeAmount = outstandingChargeAmount;
+        this.runningBalance = runningBalance;
+        this.reversed = reversed;
+        this.transfer = transfer;
+        this.paymentTypeOptions = paymentTypeOptions;
+        if (submittedOnDate != null) {
+            this.submittedOnDate = Date.from(submittedOnDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        } else {
+            this.submittedOnDate = null;
+        }
+
+        this.interestedPostedAsOn = interestedPostedAsOn;
+        this.submittedByUsername = null;
+        this.note = null;
+        this.cumulativeBalance = cumulativeBalance;
+        this.isManualTransaction = false;
     }
 
     public static SavingsAccountTransactionData withWithDrawalTransactionDetails(
@@ -225,4 +886,9 @@ public final class SavingsAccountTransactionData implements Serializable {
                 savingsAccountTransactionData.interestedPostedAsOn, savingsAccountTransactionData.submittedByUsername,
                 savingsAccountTransactionData.note);
     }
+
+    public void setId(final Long id) {
+        this.id = id;
+        this.modifiedId = id;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java
index 6168435..46157fc 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionEnumData.java
@@ -100,6 +100,10 @@ public class SavingsAccountTransactionEnumData implements Serializable {
         return this.interestPosting;
     }
 
+    public boolean isIncomeFromInterest() {
+        return this.overdraftInterest;
+    }
+
     public boolean isFeeDeduction() {
         return this.feeDeduction;
     }
@@ -152,4 +156,24 @@ public class SavingsAccountTransactionEnumData implements Serializable {
         return this.amountRelease;
     }
 
+    public boolean isOverDraftInterestPosting() {
+        return this.overdraftInterest;
+    }
+
+    public boolean isChargeTransaction() {
+        return isPayCharge() || isWithdrawalFee() || isAnnualFee();
+    }
+
+    public boolean isAnnualFee() {
+        return this.value.equals(SavingsAccountTransactionType.ANNUAL_FEE.getValue().toString());
+    }
+
+    public boolean isPayCharge() {
+        return this.value.equals(SavingsAccountTransactionType.PAY_CHARGE.getValue().toString());
+    }
+
+    public boolean isWithdrawalFee() {
+        return this.value.equals(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue().toString());
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java
index ccc225b..c9ae6b1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsProductData.java
@@ -339,6 +339,67 @@ public final class SavingsProductData implements Serializable {
                 daysToDormancy, daysToEscheat, accountMappingForPayment);
     }
 
+    public static SavingsProductData createForInterestPosting(final Long id, final EnumOptionData accountingRule) {
+        return new SavingsProductData(id, accountingRule);
+    }
+
+    private SavingsProductData(final Long id, final EnumOptionData accountingRule) {
+        this.id = id;
+        this.name = null;
+        this.shortName = null;
+        this.description = null;
+        this.currency = null;
+        this.nominalAnnualInterestRate = null;
+        this.interestCompoundingPeriodType = null;
+        this.interestPostingPeriodType = null;
+        this.interestCalculationType = null;
+        this.interestCalculationDaysInYearType = null;
+        this.accountingRule = accountingRule;
+        this.minRequiredOpeningBalance = null;
+        this.lockinPeriodFrequency = null;
+        this.lockinPeriodFrequencyType = null;
+        this.withdrawalFeeForTransfers = false;
+
+        this.currencyOptions = null;
+        this.interestCompoundingPeriodTypeOptions = null;
+        this.interestPostingPeriodTypeOptions = null;
+        this.interestCalculationTypeOptions = null;
+        this.interestCalculationDaysInYearTypeOptions = null;
+        this.lockinPeriodFrequencyTypeOptions = null;
+        this.withdrawalFeeTypeOptions = null;
+
+        this.paymentTypeOptions = null;
+        this.accountingMappingOptions = null;
+        this.accountingRuleOptions = null;
+        this.accountingMappings = null;
+
+        this.paymentChannelToFundSourceMappings = null;
+
+        this.charges = null;// charges associated with Savings product
+        this.chargeOptions = null;// charges available for adding to
+        // Savings product
+        this.penaltyOptions = null;// penalties available for adding
+        // to Savings product
+
+        this.feeToIncomeAccountMappings = null;
+        this.penaltyToIncomeAccountMappings = null;
+        this.allowOverdraft = false;
+        this.overdraftLimit = null;
+        this.minRequiredBalance = null;
+        this.enforceMinRequiredBalance = false;
+        this.minBalanceForInterestCalculation = null;
+        this.nominalAnnualInterestRateOverdraft = null;
+        this.minOverdraftForInterestCalculation = null;
+        this.taxGroup = null;
+        this.withHoldTax = false;
+        this.taxGroupOptions = null;
+        this.isDormancyTrackingActive = null;
+        this.daysToInactive = null;
+        this.daysToDormancy = null;
+        this.daysToEscheat = null;
+        this.accountMappingForPayment = null;
+    }
+
     private SavingsProductData(final Long id, final String name, final String shortName, final String description,
             final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType,
             final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
@@ -509,4 +570,21 @@ public final class SavingsProductData implements Serializable {
     public boolean isWithdrawalFeeForTransfers() {
         return withdrawalFeeForTransfers;
     }
+
+    public boolean isCashBasedAccountingEnabled() {
+        return AccountingRuleType.CASH_BASED.getValue().toString().equals(this.accountingRule.getValue());
+    }
+
+    public boolean isAccrualBasedAccountingEnabled() {
+        return isUpfrontAccrualAccounting() || isPeriodicAccrualAccounting();
+    }
+
+    public boolean isUpfrontAccrualAccounting() {
+        return AccountingRuleType.ACCRUAL_UPFRONT.getValue().toString().equals(this.accountingRule.getValue());
+    }
+
+    public boolean isPeriodicAccrualAccounting() {
+        return AccountingRuleType.ACCRUAL_PERIODIC.getValue().toString().equals(this.accountingRule.getValue());
+    }
+
 }
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 94192df..da03341 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
@@ -1003,7 +1003,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
                     if (overdraftAmount.isGreaterThanZero()) {
                         accountTransaction.updateOverdraftAmount(overdraftAmount.getAmount());
                     }
-                    accountTransaction.updateRunningBalance(runningBalance);
+                    // accountTransaction.updateRunningBalance(runningBalance);
                     if (backdatedTxnsAllowedTill) {
                         addTransactionToExisting(accountTransaction);
                     } else {
@@ -1108,6 +1108,11 @@ public class SavingsAccount extends AbstractPersistableCustom {
             this.sub_status = SavingsAccountSubStatusEnum.NONE.getValue();
         }
 
+        if (backdatedTxnsAllowedTill) {
+            this.summary.updateSummaryWithPivotConfig(this.currency, this.savingsAccountTransactionSummaryWrapper, transaction,
+                    this.savingsAccountTransactions);
+        }
+
         return transaction;
     }
 
@@ -1119,10 +1124,18 @@ public class SavingsAccount extends AbstractPersistableCustom {
         return activationLocalDate;
     }
 
+    public AppUser getActivatedBy() {
+        return this.activatedBy;
+    }
+
     public LocalDate getWithdrawnOnDate() {
         return withdrawnOnDate == null ? null : LocalDate.ofInstant(withdrawnOnDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
     }
 
+    public AppUser getWithdrawnBy() {
+        return this.withdrawnBy;
+    }
+
     // startInterestCalculationDate is set during migration so that there is no
     // interference with interest posting of previous system
     public LocalDate getStartInterestCalculationDate() {
@@ -1950,6 +1963,10 @@ public class SavingsAccount extends AbstractPersistableCustom {
         return gsim;
     }
 
+    public Long getSavingsProductId() {
+        return this.savingsProduct().getId();
+    }
+
     public void setGsim(GroupSavingsIndividualMonitoring gsim) {
         this.gsim = gsim;
     }
@@ -2063,6 +2080,10 @@ public class SavingsAccount extends AbstractPersistableCustom {
         return rejectedOnDate == null ? null : LocalDate.ofInstant(rejectedOnDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
     }
 
+    public AppUser getRejectedBy() {
+        return this.rejectedBy;
+    }
+
     public void removeSavingsOfficer(final LocalDate unassignDate) {
 
         final SavingsOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
@@ -2169,6 +2190,18 @@ public class SavingsAccount extends AbstractPersistableCustom {
         return this.nominalAnnualInterestRate;
     }
 
+    public Integer getInterestCompoundingPeriodType() {
+        return this.interestCompoundingPeriodType;
+    }
+
+    public Integer getInterestPostingPeriodType() {
+        return this.interestPostingPeriodType;
+    }
+
+    public Integer getInterestCalculationType() {
+        return this.interestCalculationType;
+    }
+
     public BigDecimal getNominalAnnualInterestRateOverdraft() {
         return this.nominalAnnualInterestRateOverdraft;
     }
@@ -2775,6 +2808,10 @@ public class SavingsAccount extends AbstractPersistableCustom {
         return this.closedOnDate == null ? null : LocalDate.ofInstant(this.closedOnDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
     }
 
+    public AppUser getClosedBy() {
+        return this.closedBy;
+    }
+
     public SavingsAccountSummary getSummary() {
         return this.summary;
     }
@@ -3213,6 +3250,14 @@ public class SavingsAccount extends AbstractPersistableCustom {
         }
     }
 
+    public AppUser getSubmittedBy() {
+        return this.submittedBy;
+    }
+
+    public AppUser getApprovedBy() {
+        return this.approvedBy;
+    }
+
     public boolean allowDeposit() {
         return true;
     }
@@ -3665,4 +3710,68 @@ public class SavingsAccount extends AbstractPersistableCustom {
     private boolean isOverdraft() {
         return allowOverdraft;
     }
+
+    public Long getGroupId() {
+        return this.groupId();
+    }
+
+    public Integer getInterestCalculationDaysInYearType() {
+        return this.interestCalculationDaysInYearType;
+    }
+
+    public BigDecimal getMinRequiredOpeningBalance() {
+        return this.minRequiredOpeningBalance;
+    }
+
+    public Integer getLockinPeriodFrequency() {
+        return this.lockinPeriodFrequency;
+    }
+
+    public Integer getLockinPeriodFrequencyType() {
+        return this.lockinPeriodFrequencyType;
+    }
+
+    public boolean isWithdrawalFeeForTransfer() {
+        return this.withdrawalFeeApplicableForTransfer;
+    }
+
+    public boolean isAllowOverdraft() {
+        return this.allowOverdraft;
+    }
+
+    public BigDecimal getOverdraftLimit() {
+        return this.overdraftLimit;
+    }
+
+    public BigDecimal getMinOverdraftForInterestCalculation() {
+        return this.minOverdraftForInterestCalculation;
+    }
+
+    public Date getLockedInUntilDate() {
+        return this.lockedInUntilDate;
+    }
+
+    public Integer getDepositType() {
+        return this.depositType;
+    }
+
+    public BigDecimal getMinRequiredBalance() {
+        return this.minRequiredBalance;
+    }
+
+    public boolean isEnforceMinRequiredBalance() {
+        return this.enforceMinRequiredBalance;
+    }
+
+    public BigDecimal getMinBalanceForInterestCalculation() {
+        return this.minBalanceForInterestCalculation;
+    }
+
+    public int getVersion() {
+        return this.version;
+    }
+
+    public boolean isWithHoldTax() {
+        return this.withHoldTax;
+    }
 }
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 17eab63..0e08398 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
@@ -73,6 +73,7 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
 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.exception.SavingsProductNotFoundException;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.slf4j.Logger;
@@ -361,6 +362,18 @@ public class SavingsAccountAssembler {
         return account;
     }
 
+    public SavingsAccountData assembleSavings(final SavingsAccountData account) {
+
+        // Update last running balance on account level
+        if (account.getTransactions() != null && account.getTransactions().size() != 0) {
+            account.getSummary().setRunningBalanceOnPivotDate(account.getTransactions().get(account.getTransactions().size() - 1)
+                    .getRunningBalance(account.getCurrency()).getAmount());
+        }
+
+        account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
+        return account;
+    }
+
     public boolean getPivotConfigStatus() {
         return this.configurationDomainService.retrievePivotDateConfig();
     }
@@ -425,4 +438,8 @@ public class SavingsAccountAssembler {
     public void assignSavingAccountHelpers(final SavingsAccount savingsAccount) {
         savingsAccount.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
     }
+
+    public void assignSavingAccountHelpers(final SavingsAccountData savingsAccountData) {
+        savingsAccountData.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountChargesPaidByData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountChargesPaidByData.java
new file mode 100644
index 0000000..2ed84b5
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountChargesPaidByData.java
@@ -0,0 +1,64 @@
+/**
+ * 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.portfolio.savings.domain;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
+
+public class SavingsAccountChargesPaidByData implements Serializable {
+
+    private final Long chargeId;
+    private final BigDecimal amount;
+    private SavingsAccountChargeData savingsAccountChargeData;
+
+    public SavingsAccountChargesPaidByData(final Long chargeId, final BigDecimal amount) {
+        this.chargeId = chargeId;
+        this.amount = amount;
+    }
+
+    public BigDecimal getAmount() {
+        return this.amount;
+    }
+
+    public Long getChargeId() {
+        return this.chargeId;
+    }
+
+    public void setSavingsAccountChargeData(final SavingsAccountChargeData savingsAccountChargeData) {
+        this.savingsAccountChargeData = savingsAccountChargeData;
+    }
+
+    public static SavingsAccountChargesPaidByData instance(final Long savingsAccountChargeId, final BigDecimal amount) {
+        return new SavingsAccountChargesPaidByData(savingsAccountChargeId, amount);
+    }
+
+    public boolean isFeeCharge() {
+        return (this.savingsAccountChargeData == null) ? false : this.savingsAccountChargeData.isFeeCharge();
+    }
+
+    public boolean isPenaltyCharge() {
+        return (this.savingsAccountChargeData == null) ? false : this.savingsAccountChargeData.isPenaltyCharge();
+    }
+
+    public SavingsAccountChargeData getSavingsAccountCharge() {
+        return this.savingsAccountChargeData;
+    }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java
index f031a0c..3e63511 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepositoryWrapper.java
@@ -170,4 +170,5 @@ public class SavingsAccountRepositoryWrapper {
             account.loadLazyCollections();
         }
     }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java
index 9bddc61..67f1913 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java
@@ -286,4 +286,39 @@ public final class SavingsAccountSummary {
         return this.runningBalanceOnInterestPostingTillDate;
     }
 
+    public BigDecimal getTotalWithdrawals() {
+        return this.totalWithdrawals;
+    }
+
+    public BigDecimal getTotalDeposits() {
+        return this.totalDeposits;
+    }
+
+    public BigDecimal getTotalWithdrawalFees() {
+        return this.totalWithdrawalFees;
+    }
+
+    public BigDecimal getTotalFeeCharge() {
+        return this.totalFeeCharge;
+    }
+
+    public BigDecimal getTotalPenaltyCharge() {
+        return this.totalPenaltyCharge;
+    }
+
+    public BigDecimal getTotalAnnualFees() {
+        return this.totalAnnualFees;
+    }
+
+    public BigDecimal getTotalInterestEarned() {
+        return this.totalInterestEarned;
+    }
+
+    public BigDecimal getTotalOverdraftInterestDerived() {
+        return this.totalOverdraftInterestDerived;
+    }
+
+    public BigDecimal getTotalWithholdTax() {
+        return this.totalWithholdTax;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
index b2b71a2..c190ef5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
@@ -201,6 +201,14 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
                 isReversed, appUser, isManualTransaction);
     }
 
+    public static SavingsAccountTransaction from(final Integer transactionTypeEnum, final LocalDate transactionDate,
+            final BigDecimal amount, final boolean isReversed, final BigDecimal runningBalance, final BigDecimal cumulativeBalance,
+            final LocalDate balanceEndDate, final Integer balanceNumberOfDays, final BigDecimal overdraftAmount,
+            final LocalDate createdDate, final boolean isManualTransaction, final Long releaseIdOfHoldAmountTransaction) {
+        return new SavingsAccountTransaction(transactionTypeEnum, transactionDate, amount, isReversed, runningBalance, cumulativeBalance,
+                balanceEndDate, balanceNumberOfDays, overdraftAmount, createdDate, isManualTransaction, releaseIdOfHoldAmountTransaction);
+    }
+
     public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
             final Money amount, final AppUser appUser) {
         final boolean isReversed = false;
@@ -301,6 +309,24 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
         this.isManualTransaction = isManualTransaction;
     }
 
+    private SavingsAccountTransaction(final Integer transactionTypeEnum, final LocalDate transactionDate, final BigDecimal amount,
+            final boolean isReversed, final BigDecimal runningBalance, final BigDecimal cumulativeBalance, final LocalDate balanceEndDate,
+            final Integer balanceNumberOfDays, final BigDecimal overdraftAmount, final LocalDate createdDate,
+            final boolean isManualTransaction, final Long releaseIdOfHoldAmountTransaction) {
+        this.typeOf = transactionTypeEnum;
+        this.dateOf = Date.from(transactionDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        this.amount = amount;
+        this.reversed = isReversed;
+        this.runningBalance = runningBalance;
+        this.cumulativeBalance = cumulativeBalance;
+        this.balanceEndDate = Date.from(balanceEndDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        this.balanceNumberOfDays = balanceNumberOfDays;
+        this.overdraftAmount = overdraftAmount;
+        this.createdDate = Date.from(createdDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        this.isManualTransaction = isManualTransaction;
+        this.releaseIdOfHoldAmountTransaction = releaseIdOfHoldAmountTransaction;
+    }
+
     public static SavingsAccountTransaction holdAmount(final SavingsAccount savingsAccount, final Office office,
             final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
         final boolean isReversed = false;
@@ -809,4 +835,32 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
     public boolean isAmountOnHoldNotReleased() {
         return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null);
     }
+
+    public Long getOfficeId() {
+        return this.office.getId();
+    }
+
+    public Date getBalanceEndDate() {
+        return this.balanceEndDate;
+    }
+
+    public BigDecimal getCumulativeBalance() {
+        return this.cumulativeBalance;
+    }
+
+    public Integer getBalanceNumberOfDays() {
+        return this.balanceNumberOfDays;
+    }
+
+    public Long getAppUserId() {
+        return this.appUser.getId();
+    }
+
+    public Date getCreatedDate() {
+        return this.createdDate;
+    }
+
+    public boolean getIsManualTransaction() {
+        return this.isManualTransaction;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionDataComparator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionDataComparator.java
new file mode 100644
index 0000000..223b819
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionDataComparator.java
@@ -0,0 +1,44 @@
+/**
+ * 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.portfolio.savings.domain;
+
+import java.util.Comparator;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
+
+public class SavingsAccountTransactionDataComparator implements Comparator<SavingsAccountTransactionData> {
+
+    @Override
+    public int compare(final SavingsAccountTransactionData o1, final SavingsAccountTransactionData o2) {
+        int compareResult = 0;
+
+        final int comparsion = o1.getTransactionDate().compareTo(o2.getLastTransactionDate());
+        if (comparsion == 0) {
+            compareResult = o1.getSubmittedOnDate().compareTo(o2.getSubmittedOnDate());
+            if (compareResult == 0 && o1.getId() != null && o2.getId() != null) {
+                compareResult = o1.getId().compareTo(o2.getId());
+            } else {
+                compareResult = comparsion;
+            }
+        } else {
+            compareResult = comparsion;
+        }
+        return compareResult;
+    }
+
+}
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 03ffb8e..796653c 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
@@ -20,8 +20,10 @@ package org.apache.fineract.portfolio.savings.domain;
 
 import java.math.BigDecimal;
 import java.util.List;
+import org.apache.fineract.organisation.monetary.data.CurrencyData;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 import org.springframework.stereotype.Component;
 
 /**
@@ -41,6 +43,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalDeposits(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isDepositAndNotReversed() || transaction.isDividendPayoutAndNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalWithdrawals(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
         for (final SavingsAccountTransaction transaction : transactions) {
@@ -51,6 +63,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalWithdrawals(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isWithdrawal() && transaction.isNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalInterestPosted(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
         for (final SavingsAccountTransaction transaction : transactions) {
@@ -61,6 +83,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalInterestPosted(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isInterestPostingAndNotReversed() && transaction.isNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalInterestPosted(final MonetaryCurrency currency, final BigDecimal currentInterestPosted,
             final List<SavingsAccountTransaction> savingsAccountTransactions) {
         Money total = Money.of(currency, currentInterestPosted);
@@ -82,6 +114,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalWithdrawalFees(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isWithdrawalFeeAndNotReversed() && transaction.isNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalAnnualFees(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
         for (final SavingsAccountTransaction transaction : transactions) {
@@ -92,6 +134,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalAnnualFees(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isAnnualFeeAndNotReversed() && transaction.isNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalFeesCharge(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
         for (final SavingsAccountTransaction transaction : transactions) {
@@ -102,6 +154,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalFeesCharge(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isFeeChargeAndNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalFeesChargeWaived(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
         for (final SavingsAccountTransaction transaction : transactions) {
@@ -112,6 +174,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalFeesChargeWaived(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isWaiveFeeChargeAndNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalPenaltyCharge(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
         for (final SavingsAccountTransaction transaction : transactions) {
@@ -122,6 +194,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalPenaltyCharge(final CurrencyData currency, final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isPenaltyChargeAndNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalPenaltyChargeWaived(final MonetaryCurrency currency,
             final List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
@@ -133,6 +215,17 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalPenaltyChargeWaived(final CurrencyData currency,
+            final List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isWaivePenaltyChargeAndNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalOverdraftInterest(MonetaryCurrency currency, BigDecimal overdraftPosted,
             List<SavingsAccountTransaction> transactions) {
         Money total = Money.of(currency, overdraftPosted);
@@ -154,6 +247,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
         return total.getAmountDefaultedToNullIfZero();
     }
 
+    public BigDecimal calculateTotalOverdraftInterest(CurrencyData currency, List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isOverdraftInterestAndNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
+
     public BigDecimal calculateTotalWithholdTaxWithdrawal(MonetaryCurrency currency, List<SavingsAccountTransaction> transactions) {
         Money total = Money.zero(currency);
         for (final SavingsAccountTransaction transaction : transactions) {
@@ -163,4 +266,14 @@ public final class SavingsAccountTransactionSummaryWrapper {
         }
         return total.getAmountDefaultedToNullIfZero();
     }
+
+    public BigDecimal calculateTotalWithholdTaxWithdrawal(CurrencyData currency, List<SavingsAccountTransactionData> transactions) {
+        Money total = Money.zero(currency);
+        for (final SavingsAccountTransactionData transaction : transactions) {
+            if (transaction.isWithHoldTaxAndNotReversed()) {
+                total = total.plus(transaction.getAmount());
+            }
+        }
+        return total.getAmountDefaultedToNullIfZero();
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java
index 4d5a5ff..98dcf49 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java
@@ -32,6 +32,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
 import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
 
 public final class PostingPeriod {
@@ -96,6 +97,7 @@ public final class PostingPeriod {
         boolean interestTransfered = false;
         Money openingDayBalance = periodStartingBalance;
         Money closeOfDayBalance = openingDayBalance;
+
         for (final SavingsAccountTransaction transaction : orderedListOfTransactions) {
 
             if (transaction.fallsWithin(periodInterval)) {
@@ -155,6 +157,79 @@ public final class PostingPeriod {
                 minOverdraftForInterestCalculation, isUserPosting, financialYearBeginningMonth);
     }
 
+    public static PostingPeriod createFromDTO(final LocalDateInterval periodInterval, final Money periodStartingBalance,
+            final List<SavingsAccountTransactionData> orderedListOfTransactions, final MonetaryCurrency currency,
+            final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
+            final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
+            final LocalDate upToInterestCalculationDate, Collection<Long> interestPostTransactions, boolean isInterestTransfer,
+            final Money minBalanceForInterestCalculation, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
+            final BigDecimal overdraftInterestRateAsFraction, final Money minOverdraftForInterestCalculation, boolean isUserPosting,
+            int financialYearBeginningMonth) {
+
+        final List<EndOfDayBalance> accountEndOfDayBalances = new ArrayList<>();
+        boolean interestTransfered = false;
+        Money openingDayBalance = periodStartingBalance;
+        Money closeOfDayBalance = openingDayBalance;
+
+        for (final SavingsAccountTransactionData transaction : orderedListOfTransactions) {
+
+            if (transaction.fallsWithin(periodInterval)) {
+                // the balance of the transaction falls entirely within this
+                // period so no need to do any cropping/bounding
+                final EndOfDayBalance endOfDayBalance = transaction.toEndOfDayBalance(openingDayBalance);
+                accountEndOfDayBalances.add(endOfDayBalance);
+
+                openingDayBalance = endOfDayBalance.closingBalance();
+
+            } else if (transaction.spansAnyPortionOf(periodInterval)) {
+                final EndOfDayBalance endOfDayBalance = transaction.toEndOfDayBalanceBoundedBy(openingDayBalance, periodInterval);
+                accountEndOfDayBalances.add(endOfDayBalance);
+
+                closeOfDayBalance = endOfDayBalance.closingBalance();
+                openingDayBalance = closeOfDayBalance;
+            }
+
+            // this check is to make sure to add interest if withdrawal is
+            // happened for already
+            if (transaction.occursOn(periodInterval.endDate().plusDays(1))) {
+                if (transaction.getId() == null) {
+                    interestTransfered = isInterestTransfer;
+                } else if (interestPostTransactions.contains(transaction.getId())) {
+                    interestTransfered = true;
+                }
+            }
+
+        }
+
+        if (accountEndOfDayBalances.isEmpty()) {
+            LocalDate balanceStartDate = periodInterval.startDate();
+            LocalDate balanceEndDate = periodInterval.endDate();
+            Integer numberOfDaysOfBalance = periodInterval.daysInPeriodInclusiveOfEndDate();
+
+            if (balanceEndDate.isAfter(upToInterestCalculationDate)) {
+                balanceEndDate = upToInterestCalculationDate;
+                final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
+                numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
+            }
+
+            final EndOfDayBalance endOfDayBalance = EndOfDayBalance.from(balanceStartDate, openingDayBalance, closeOfDayBalance,
+                    numberOfDaysOfBalance);
+
+            accountEndOfDayBalances.add(endOfDayBalance);
+
+            closeOfDayBalance = endOfDayBalance.closingBalance();
+            openingDayBalance = closeOfDayBalance;
+        }
+
+        final List<CompoundingPeriod> compoundingPeriods = compoundingPeriodsInPostingPeriod(periodInterval, interestCompoundingPeriodType,
+                accountEndOfDayBalances, upToInterestCalculationDate, financialYearBeginningMonth);
+
+        return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType,
+                interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods, interestTransfered,
+                minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd, overdraftInterestRateAsFraction,
+                minOverdraftForInterestCalculation, isUserPosting, financialYearBeginningMonth);
+    }
+
     private PostingPeriod(final LocalDateInterval periodInterval, final MonetaryCurrency currency, final Money openingBalance,
             final Money closingBalance, final SavingsCompoundingInterestPeriodType interestCompoundingType,
             final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSavings.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingService.java
old mode 100755
new mode 100644
similarity index 58%
copy from fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSavings.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingService.java
index dbaf6e1..26c05c3
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorForSavings.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingService.java
@@ -16,12 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.accounting.journalentry.service;
+package org.apache.fineract.portfolio.savings.service;
 
-import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
+import java.math.MathContext;
+import java.time.LocalDate;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
 
-public interface AccountingProcessorForSavings {
+public interface SavingsAccountInterestPostingService {
 
-    void createJournalEntriesForSavings(SavingsDTO savingsDTO);
+    SavingsAccountData postInterest(MathContext mc, LocalDate interestPostingUpToDate, boolean isInterestTransfer,
+            boolean isSavingsInterestPostingAtCurrentPeriodEnd, Integer financialYearBeginningMonth, LocalDate postInterestOnDate,
+            boolean backdatedTxnsAllowedTill, SavingsAccountData savingsAccountData);
 
 }
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
new file mode 100644
index 0000000..dd660ff
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
@@ -0,0 +1,564 @@
+/**
+ * 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.portfolio.savings.service;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+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.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.savings.DepositAccountType;
+import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
+import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
+import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
+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.domain.SavingsAccountChargesPaidByData;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionDataComparator;
+import org.apache.fineract.portfolio.savings.domain.SavingsHelper;
+import org.apache.fineract.portfolio.savings.domain.interest.PostingPeriod;
+import org.apache.fineract.portfolio.tax.data.TaxComponentData;
+import org.apache.fineract.portfolio.tax.service.TaxUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountInterestPostingService {
+
+    private final SavingsHelper savingsHelper;
+
+    @Autowired
+    public SavingsAccountInterestPostingServiceImpl(final SavingsHelper savingsHelper) {
+        this.savingsHelper = savingsHelper;
+    }
+
+    @Override
+    public SavingsAccountData postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer,
+            final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
+            final LocalDate postInterestOnDate, final boolean backdatedTxnsAllowedTill, final SavingsAccountData savingsAccountData) {
+
+        Money interestPostedToDate = Money.zero(savingsAccountData.currency());
+        LocalDate startInterestDate = getStartInterestCalculationDate(savingsAccountData);
+
+        if (backdatedTxnsAllowedTill && savingsAccountData.getSummary().getInterestPostedTillDate() != null) {
+            interestPostedToDate = Money.of(savingsAccountData.currency(), savingsAccountData.getSummary().getTotalInterestPosted());
+            SavingsAccountTransactionData savingsAccountTransactionData = retrieveLastTransactions(savingsAccountData);
+            Date lastTransactionDate = Date.from(
+                    savingsAccountTransactionData.getLastTransactionDate().atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant());
+            savingsAccountData.setStartInterestCalculationDate(lastTransactionDate);
+        } else {
+            savingsAccountData.setStartInterestCalculationDate(
+                    Date.from(startInterestDate.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()));
+        }
+
+        final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate, isInterestTransfer,
+                isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill,
+                savingsAccountData);
+
+        boolean recalucateDailyBalanceDetails = false;
+        boolean applyWithHoldTax = isWithHoldTaxApplicableForInterestPosting(savingsAccountData);
+        final List<SavingsAccountTransactionData> withholdTransactions = new ArrayList<>();
+
+        withholdTransactions.addAll(findWithHoldSavingsTransactionsWithPivotConfig(savingsAccountData));
+
+        for (final PostingPeriod interestPostingPeriod : postingPeriods) {
+
+            final LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction();
+            final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();
+
+            if (!interestPostingTransactionDate.isAfter(interestPostingUpToDate)) {
+                interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod);
+
+                final SavingsAccountTransactionData postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate,
+                        savingsAccountData);
+
+                if (postingTransaction == null) {
+                    SavingsAccountTransactionData newPostingTransaction;
+                    if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(savingsAccountData.currency()))) {
+                        newPostingTransaction = SavingsAccountTransactionData.interestPosting(savingsAccountData,
+                                interestPostingTransactionDate, interestEarnedToBePostedForPeriod, interestPostingPeriod.isUserPosting());
+                    } else {
+                        newPostingTransaction = SavingsAccountTransactionData.interestPosting(savingsAccountData,
+                                interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated(),
+                                interestPostingPeriod.isUserPosting());
+                    }
+
+                    savingsAccountData.updateTransactions(newPostingTransaction);
+
+                    if (applyWithHoldTax) {
+                        createWithHoldTransaction(interestEarnedToBePostedForPeriod.getAmount(), interestPostingTransactionDate,
+                                savingsAccountData);
+                    }
+                    recalucateDailyBalanceDetails = true;
+                } else {
+                    boolean correctionRequired = false;
+                    if (postingTransaction.isInterestPostingAndNotReversed()) {
+                        correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod);
+                    } else {
+                        correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod.negated());
+                    }
+                    if (correctionRequired) {
+                        boolean applyWithHoldTaxForOldTransaction = false;
+                        postingTransaction.reverse();
+
+                        final SavingsAccountTransactionData withholdTransaction = findTransactionFor(interestPostingTransactionDate,
+                                withholdTransactions);
+
+                        if (withholdTransaction != null) {
+                            withholdTransaction.reverse();
+                            applyWithHoldTaxForOldTransaction = true;
+                        }
+                        SavingsAccountTransactionData newPostingTransaction;
+                        if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(savingsAccountData.currency()))) {
+                            newPostingTransaction = SavingsAccountTransactionData.interestPosting(savingsAccountData,
+                                    interestPostingTransactionDate, interestEarnedToBePostedForPeriod,
+                                    interestPostingPeriod.isUserPosting());
+                        } else {
+                            newPostingTransaction = SavingsAccountTransactionData.overdraftInterest(savingsAccountData,
+                                    interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated(),
+                                    interestPostingPeriod.isUserPosting());
+                        }
+
+                        savingsAccountData.updateTransactions(newPostingTransaction);
+
+                        if (applyWithHoldTaxForOldTransaction) {
+                            createWithHoldTransaction(interestEarnedToBePostedForPeriod.getAmount(), interestPostingTransactionDate,
+                                    savingsAccountData);
+                        }
+                        recalucateDailyBalanceDetails = true;
+                    }
+                }
+            }
+        }
+
+        if (recalucateDailyBalanceDetails) {
+            // no openingBalance concept supported yet but probably will to
+            // allow
+            // for migrations.
+            Money openingAccountBalance = Money.zero(savingsAccountData.currency());
+
+            if (backdatedTxnsAllowedTill) {
+                if (savingsAccountData.getSummary().getLastInterestCalculationDate() == null) {
+                    openingAccountBalance = Money.zero(savingsAccountData.currency());
+                } else {
+                    openingAccountBalance = Money.of(savingsAccountData.currency(),
+                            savingsAccountData.getSummary().getRunningBalanceOnPivotDate());
+                }
+            }
+
+            // update existing transactions so derived balance fields are
+            // correct.
+            recalculateDailyBalances(openingAccountBalance, interestPostingUpToDate, backdatedTxnsAllowedTill, savingsAccountData);
+        }
+
+        if (!backdatedTxnsAllowedTill) {
+            savingsAccountData.getSummary().updateSummary(savingsAccountData.currency(),
+                    savingsAccountData.getSavingsAccountTransactionSummaryWrapper(), savingsAccountData.getSavingsAccountTransactionData());
+        } else {
+            savingsAccountData.getSummary().updateSummaryWithPivotConfig(savingsAccountData.currency(),
+                    savingsAccountData.getSavingsAccountTransactionSummaryWrapper(), null,
+                    savingsAccountData.getSavingsAccountTransactionData());
+        }
+
+        return savingsAccountData;
+    }
+
+    protected SavingsAccountTransactionData findTransactionFor(final LocalDate postingDate,
+            final List<SavingsAccountTransactionData> transactions) {
+        SavingsAccountTransactionData transaction = null;
+        for (final SavingsAccountTransactionData savingsAccountTransaction : transactions) {
+            if (savingsAccountTransaction.occursOn(postingDate)) {
+                transaction = savingsAccountTransaction;
+                break;
+            }
+        }
+        return transaction;
+    }
+
+    public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate,
+            boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
+            final LocalDate postInterestOnDate, final boolean backdatedTxnsAllowedTill, final SavingsAccountData savingsAccountData) {
+
+        // no openingBalance concept supported yet but probably will to allow
+        // for migrations.
+        Money openingAccountBalance = null;
+
+        // Check global configurations and 'pivot' date is null
+        if (backdatedTxnsAllowedTill) {
+            openingAccountBalance = Money.of(savingsAccountData.currency(), savingsAccountData.getSummary().getRunningBalanceOnPivotDate());
+        } else {
+            openingAccountBalance = Money.zero(savingsAccountData.currency());
+        }
+
+        // update existing transactions so derived balance fields are
+        // correct.
+        recalculateDailyBalances(openingAccountBalance, upToInterestCalculationDate, backdatedTxnsAllowedTill, savingsAccountData);
+
+        // 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(savingsAccountData.getInterestPostingPeriodType());
+
+        final SavingsCompoundingInterestPeriodType compoundingPeriodType = SavingsCompoundingInterestPeriodType
+                .fromInt(savingsAccountData.getInterestCompoundingPeriodType());
+
+        final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
+                .fromInt(savingsAccountData.getInterestCalculationDaysInYearType());
+
+        List<LocalDate> postedAsOnDates = getManualPostingDates(savingsAccountData);
+        if (postInterestOnDate != null) {
+            postedAsOnDates.add(postInterestOnDate);
+        }
+        final List<LocalDateInterval> postingPeriodIntervals = this.savingsHelper.determineInterestPostingPeriods(
+                savingsAccountData.getStartInterestCalculationDate(), upToInterestCalculationDate, postingPeriodType,
+                financialYearBeginningMonth, postedAsOnDates);
+
+        final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
+
+        Money periodStartingBalance;
+        if (savingsAccountData.getStartInterestCalculationDate() != null) {
+            final SavingsAccountTransactionData transaction = retrieveLastTransactions(savingsAccountData);
+
+            if (transaction == null) {
+                final String defaultUserMessage = "No transactions were found on the specified date "
+                        + savingsAccountData.getStartInterestCalculationDate().toString() + " for account number "
+                        + savingsAccountData.getAccountNo() + " and resource id " + savingsAccountData.getId();
+
+                final ApiParameterError error = ApiParameterError.parameterError(
+                        "error.msg.savingsaccount.transaction.incorrect.start.interest.calculation.date", defaultUserMessage,
+                        "transactionDate", savingsAccountData.getStartInterestCalculationDate().toString());
+
+                final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+                dataValidationErrors.add(error);
+
+                throw new PlatformApiDataValidationException(dataValidationErrors);
+            }
+
+            periodStartingBalance = transaction.getRunningBalance(savingsAccountData.currency());
+        } else {
+            periodStartingBalance = Money.zero(savingsAccountData.currency());
+        }
+
+        final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType
+                .fromInt(savingsAccountData.getInterestCalculationType());
+        final BigDecimal interestRateAsFraction = getEffectiveInterestRateAsFraction(mc, upToInterestCalculationDate, savingsAccountData);
+        final BigDecimal overdraftInterestRateAsFraction = getEffectiveOverdraftInterestRateAsFraction(mc, savingsAccountData);
+        final Collection<Long> interestPostTransactions = this.savingsHelper.fetchPostInterestTransactionIds(savingsAccountData.getId());
+        final Money minBalanceForInterestCalculation = Money.of(savingsAccountData.currency(),
+                minBalanceForInterestCalculation(savingsAccountData));
+        final Money minOverdraftForInterestCalculation = Money.of(savingsAccountData.currency(),
+                savingsAccountData.getMinOverdraftForInterestCalculation());
+        final MonetaryCurrency monetaryCurrency = MonetaryCurrency.fromCurrencyData(savingsAccountData.currency());
+
+        for (final LocalDateInterval periodInterval : postingPeriodIntervals) {
+
+            boolean isUserPosting = false;
+            if (postedAsOnDates.contains(periodInterval.endDate().plusDays(1))) {
+                isUserPosting = true;
+            }
+            final PostingPeriod postingPeriod = PostingPeriod.createFromDTO(periodInterval, periodStartingBalance,
+                    retreiveOrderedNonInterestPostingTransactions(savingsAccountData), monetaryCurrency, compoundingPeriodType,
+                    interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate,
+                    interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
+                    isSavingsInterestPostingAtCurrentPeriodEnd, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
+                    isUserPosting, financialYearBeginningMonth);
+
+            periodStartingBalance = postingPeriod.closingBalance();
+
+            allPostingPeriods.add(postingPeriod);
+        }
+
+        this.savingsHelper.calculateInterestForAllPostingPeriods(monetaryCurrency, allPostingPeriods,
+                getLockedInUntilLocalDate(savingsAccountData), false);
+
+        savingsAccountData.getSummary().updateFromInterestPeriodSummaries(monetaryCurrency, allPostingPeriods);
+
+        if (backdatedTxnsAllowedTill) {
+            savingsAccountData.getSummary().updateSummaryWithPivotConfig(savingsAccountData.currency(),
+                    savingsAccountData.getSavingsAccountTransactionSummaryWrapper(), null, savingsAccountData.getTransactions());
+        } else {
+            savingsAccountData.getSummary().updateSummary(savingsAccountData.currency(),
+                    savingsAccountData.getSavingsAccountTransactionSummaryWrapper(), savingsAccountData.getTransactions());
+        }
+
+        return allPostingPeriods;
+    }
+
+    private List<SavingsAccountTransactionData> retreiveOrderedNonInterestPostingTransactions(final SavingsAccountData savingsAccountData) {
+        final List<SavingsAccountTransactionData> listOfTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
+
+        final List<SavingsAccountTransactionData> orderedNonInterestPostingTransactions = new ArrayList<>();
+
+        for (final SavingsAccountTransactionData transaction : listOfTransactionsSorted) {
+            if (!(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
+                    && transaction.isNotReversed()) {
+                orderedNonInterestPostingTransactions.add(transaction);
+            }
+        }
+        orderedNonInterestPostingTransactions.sort(new SavingsAccountTransactionDataComparator());
+        return orderedNonInterestPostingTransactions;
+    }
+
+    private List<SavingsAccountTransactionData> retreiveListOfTransactions(final SavingsAccountData savingsAccountData) {
+        if (savingsAccountData.getTransactions() != null && savingsAccountData.getTransactions().size() == 1) {
+            return savingsAccountData.getTransactions();
+        }
+
+        final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>();
+        listOfTransactionsSorted.addAll(savingsAccountData.getTransactions());
+
+        final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator();
+        Collections.sort(listOfTransactionsSorted, transactionComparator);
+        return listOfTransactionsSorted;
+    }
+
+    protected LocalDate getLockedInUntilLocalDate(final SavingsAccountData savingsAccount) {
+        LocalDate lockedInUntilLocalDate = null;
+        if (savingsAccount.getLockedInUntilDate() != null) {
+            lockedInUntilLocalDate = savingsAccount.getActivationLocalDate();
+            // lockedInUntilLocalDate = LocalDate.ofInstant(this.lockedInUntilDate.toInstant(),
+            // DateUtils.getDateTimeZoneOfTenant());
+        }
+        return lockedInUntilLocalDate;
+    }
+
+    private BigDecimal minBalanceForInterestCalculation(final SavingsAccountData savingsAccountData) {
+        return savingsAccountData.getMinBalanceForInterestCalculation();
+    }
+
+    private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext mc, final SavingsAccountData savingsAccountData) {
+        return savingsAccountData.getNominalAnnualInterestRateOverdraft().divide(BigDecimal.valueOf(100L), mc);
+    }
+
+    @SuppressWarnings("unused")
+    private BigDecimal getEffectiveInterestRateAsFraction(final MathContext mc, final LocalDate upToInterestCalculationDate,
+            final SavingsAccountData savingsAccountData) {
+        return savingsAccountData.getNominalAnnualInterestRate().divide(BigDecimal.valueOf(100L), mc);
+    }
+
+    public List<SavingsAccountTransactionData> getTransactions(final SavingsAccountData savingsAccountData) {
+        return savingsAccountData.getTransactions();
+    }
+
+    private SavingsAccountTransactionData retrieveLastTransactions(final SavingsAccountData savingsAccountData) {
+        if (savingsAccountData.getTransactions() != null && savingsAccountData.getTransactions().size() == 1) {
+            return savingsAccountData.getTransactions().get(0);
+        }
+        final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>();
+        listOfTransactionsSorted.addAll(savingsAccountData.getTransactions());
+        final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator();
+        Collections.sort(listOfTransactionsSorted, transactionComparator);
+        return listOfTransactionsSorted.get(0);
+    }
+
+    public LocalDate getStartInterestCalculationDate(final SavingsAccountData savingsAccountData) {
+        LocalDate startInterestCalculationLocalDate = null;
+        if (savingsAccountData.getStartInterestCalculationDate() != null) {
+            startInterestCalculationLocalDate = savingsAccountData.getStartInterestCalculationDate();
+        } else {
+            startInterestCalculationLocalDate = getActivationLocalDate(savingsAccountData);
+        }
+        return startInterestCalculationLocalDate;
+    }
+
+    public LocalDate getActivationLocalDate(final SavingsAccountData savingsAccountData) {
+        LocalDate activationLocalDate = null;
+        if (savingsAccountData.getActivationLocalDate() != null) {
+            activationLocalDate = savingsAccountData.getActivationLocalDate();
+        }
+        return activationLocalDate;
+    }
+
+    public List<LocalDate> getManualPostingDates(final SavingsAccountData savingsAccountData) {
+        List<LocalDate> transactions = new ArrayList<>();
+        for (SavingsAccountTransactionData trans : savingsAccountData.getSavingsAccountTransactionData()) {
+            if (trans.isInterestPosting() && trans.isNotReversed() && trans.isManualTransaction()) {
+                transactions.add(trans.getTransactionLocalDate());
+            }
+        }
+        return transactions;
+    }
+
+    protected void recalculateDailyBalances(final Money openingAccountBalance, final LocalDate interestPostingUpToDate,
+            final boolean backdatedTxnsAllowedTill, final SavingsAccountData savingsAccountData) {
+
+        Money runningBalance = openingAccountBalance.copy();
+
+        List<SavingsAccountTransactionData> accountTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
+
+        boolean isTransactionsModified = false;
+
+        for (final SavingsAccountTransactionData transaction : accountTransactionsSorted) {
+            if (transaction.isReversed()) {
+                transaction.zeroBalanceFields();
+            } else {
+                Money overdraftAmount = Money.zero(savingsAccountData.currency());
+                Money transactionAmount = Money.zero(savingsAccountData.currency());
+                if (transaction.isCredit() || transaction.isAmountRelease()) {
+                    if (runningBalance.isLessThanZero()) {
+                        Money diffAmount = Money.of(savingsAccountData.currency(), transaction.getAmount()).plus(runningBalance);
+                        if (diffAmount.isGreaterThanZero()) {
+                            overdraftAmount = Money.of(savingsAccountData.currency(), transaction.getAmount()).minus(diffAmount);
+                        } else {
+                            overdraftAmount = Money.of(savingsAccountData.currency(), transaction.getAmount());
+                        }
+                    }
+                    transactionAmount = transactionAmount.plus(transaction.getAmount());
+                } else if (transaction.isDebit() || transaction.isAmountOnHold()) {
+                    if (runningBalance.isLessThanZero()) {
+                        overdraftAmount = Money.of(savingsAccountData.currency(), transaction.getAmount());
+                    }
+                    transactionAmount = transactionAmount.minus(transaction.getAmount());
+                }
+
+                runningBalance = runningBalance.plus(transactionAmount);
+                transaction.updateRunningBalance(runningBalance);
+                if (overdraftAmount.isZero() && runningBalance.isLessThanZero()) {
+                    overdraftAmount = overdraftAmount.plus(runningBalance.getAmount().negate());
+                }
+                if (transaction.getId() == null && overdraftAmount.isGreaterThanZero()) {
+                    transaction.updateOverdraftAmount(overdraftAmount.getAmount());
+                } else if (overdraftAmount.isNotEqualTo(Money.of(savingsAccountData.currency(), transaction.getOverdraftAmount()))) {
+                    SavingsAccountTransactionData accountTransaction = SavingsAccountTransactionData.copyTransaction(transaction);
+                    if (transaction.isChargeTransaction()) {
+                        Set<SavingsAccountChargesPaidByData> chargesPaidBy = transaction.getSavingsAccountChargesPaid();
+                        final Set<SavingsAccountChargesPaidByData> newChargePaidBy = new HashSet<>();
+                        chargesPaidBy.forEach(
+                                x -> newChargePaidBy.add(SavingsAccountChargesPaidByData.instance(x.getChargeId(), x.getAmount())));
+                        accountTransaction.getSavingsAccountChargesPaid().addAll(newChargePaidBy);
+                    }
+                    transaction.reverse();
+                    if (overdraftAmount.isGreaterThanZero()) {
+                        accountTransaction.updateOverdraftAmount(overdraftAmount.getAmount());
+                    }
+                    accountTransaction.updateRunningBalance(runningBalance);
+                    addTransactionToExisting(accountTransaction, savingsAccountData);
+
+                    isTransactionsModified = true;
+                }
+
+            }
+        }
+
+        if (isTransactionsModified) {
+            accountTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
+        }
+        resetAccountTransactionsEndOfDayBalances(accountTransactionsSorted, interestPostingUpToDate, savingsAccountData);
+    }
+
+    public void addTransactionToExisting(final SavingsAccountTransactionData transaction, final SavingsAccountData savingsAccountData) {
+        savingsAccountData.updateTransactions(transaction);
+    }
+
+    private List<SavingsAccountTransactionData> findWithHoldSavingsTransactionsWithPivotConfig(
+            final SavingsAccountData savingsAccountData) {
+        final List<SavingsAccountTransactionData> withholdTransactions = new ArrayList<>();
+        List<SavingsAccountTransactionData> trans = savingsAccountData.getSavingsAccountTransactionData();
+        for (final SavingsAccountTransactionData transaction : trans) {
+            if (transaction.isWithHoldTaxAndNotReversed()) {
+                withholdTransactions.add(transaction);
+            }
+        }
+        return withholdTransactions;
+    }
+
+    private boolean createWithHoldTransaction(final BigDecimal amount, final LocalDate date, final SavingsAccountData savingsAccountData) {
+        boolean isTaxAdded = false;
+        if (savingsAccountData.getTaxGroupData() != null && amount.compareTo(BigDecimal.ZERO) > 0) {
+            Map<TaxComponentData, BigDecimal> taxSplit = TaxUtils.splitTaxData(amount, date,
+                    savingsAccountData.getTaxGroupData().getTaxAssociations().stream().collect(Collectors.toSet()), amount.scale());
+            BigDecimal totalTax = TaxUtils.totalTaxDataAmount(taxSplit);
+            if (totalTax.compareTo(BigDecimal.ZERO) > 0) {
+                SavingsAccountTransactionData withholdTransaction = SavingsAccountTransactionData.withHoldTax(savingsAccountData, date,
+                        Money.of(savingsAccountData.currency(), totalTax), taxSplit);
+
+                savingsAccountData.getSavingsAccountTransactionData().add(withholdTransaction);
+                // if (backdatedTxnsAllowedTill) {
+                // addTransactionToExisting(withholdTransaction);
+                // } else {
+                // addTransaction(withholdTransaction);
+                // }
+                isTaxAdded = true;
+            }
+        }
+        return isTaxAdded;
+    }
+
+    protected SavingsAccountTransactionData findInterestPostingTransactionFor(final LocalDate postingDate,
+            final SavingsAccountData savingsAccountData) {
+        SavingsAccountTransactionData postingTransation = null;
+        List<SavingsAccountTransactionData> trans = savingsAccountData.getSavingsAccountTransactionData();
+        for (final SavingsAccountTransactionData transaction : trans) {
+            if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
+                    && transaction.occursOn(postingDate)) {
+                postingTransation = transaction;
+                break;
+            }
+        }
+        return postingTransation;
+    }
+
+    protected void resetAccountTransactionsEndOfDayBalances(final List<SavingsAccountTransactionData> accountTransactionsSorted,
+            final LocalDate interestPostingUpToDate, final SavingsAccountData savingsAccountData) {
+        // loop over transactions in reverse
+        LocalDate endOfBalanceDate = interestPostingUpToDate;
+        for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) {
+            final SavingsAccountTransactionData transaction = accountTransactionsSorted.get(i);
+            if (transaction.isNotReversed()
+                    && !(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())) {
+                transaction.updateCumulativeBalanceAndDates(MonetaryCurrency.fromCurrencyData(savingsAccountData.currency()),
+                        endOfBalanceDate);
+                // this transactions transaction date is end of balance date for
+                // previous transaction.
+                endOfBalanceDate = transaction.getTransactionLocalDate().minusDays(1);
+            }
+        }
+    }
+
+    private boolean isWithHoldTaxApplicableForInterestPosting(final SavingsAccountData savingsAccountData) {
+        return this.withHoldTax(savingsAccountData) && this.depositAccountType(savingsAccountData).isSavingsDeposit();
+    }
+
+    private boolean withHoldTax(final SavingsAccountData savingsAccountData) {
+        return savingsAccountData.withHoldTax();
+    }
+
+    public DepositAccountType depositAccountType(final SavingsAccountData savingsAccountData) {
+        return savingsAccountData.depositAccountType();
+    }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java
index 9c794fb..b425da1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformService.java
@@ -63,4 +63,9 @@ public interface SavingsAccountReadPlatformService {
     String retrieveAccountNumberByAccountId(Long accountId);
 
     List<Long> getAccountsIdsByStatusPaged(Integer status, int pageSize, Long maxSavingsIdInList);
+
+    List<SavingsAccountData> retrieveAllSavingsDataForInterestPosting(boolean backdatedTxnsAllowedTill, int pageSize, Integer status,
+            Long maxSavingsId);
+
+    List<SavingsAccountTransactionData> retrieveAllTransactionData(List<String> refNo);
 }
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 8c2923d..26e041d 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
@@ -27,8 +27,12 @@ import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.accounting.common.AccountingRuleType;
+import org.apache.fineract.accounting.glaccount.data.GLAccountData;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
@@ -69,15 +73,20 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
 import org.apache.fineract.portfolio.savings.data.SavingsProductData;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountChargesPaidByData;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum;
 import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
+import org.apache.fineract.portfolio.tax.data.TaxComponentData;
+import org.apache.fineract.portfolio.tax.data.TaxDetailsData;
 import org.apache.fineract.portfolio.tax.data.TaxGroupData;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.EmptyResultDataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.ResultSetExtractor;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
@@ -98,7 +107,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
     // mappers
     private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
     private final SavingsAccountTransactionsMapper transactionsMapper;
+    private final SavingsAccountTransactionsForBatchMapper savingsAccountTransactionsForBatchMapper;
     private final SavingAccountMapper savingAccountMapper;
+    private final SavingAccountMapperForInterestPosting savingAccountMapperForInterestPosting;
     // private final SavingsAccountAnnualFeeMapper annualFeeMapper;
 
     // pagination
@@ -106,6 +117,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
 
     private final EntityDatatableChecksReadService entityDatatableChecksReadService;
     private final ColumnValidator columnValidator;
+    private final SavingsAccountAssembler savingAccountAssembler;
 
     @Autowired
     public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
@@ -113,7 +125,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
             final SavingsProductReadPlatformService savingProductReadPlatformService,
             final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService,
             final ChargeReadPlatformService chargeReadPlatformService,
-            final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator) {
+            final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator,
+            final SavingsAccountAssembler savingAccountAssembler) {
         this.context = context;
         this.jdbcTemplate = new JdbcTemplate(dataSource);
         this.clientReadPlatformService = clientReadPlatformService;
@@ -123,11 +136,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
         this.dropdownReadPlatformService = dropdownReadPlatformService;
         this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper();
         this.transactionsMapper = new SavingsAccountTransactionsMapper();
+        this.savingsAccountTransactionsForBatchMapper = new SavingsAccountTransactionsForBatchMapper();
         this.savingAccountMapper = new SavingAccountMapper();
         // this.annualFeeMapper = new SavingsAccountAnnualFeeMapper();
         this.chargeReadPlatformService = chargeReadPlatformService;
         this.entityDatatableChecksReadService = entityDatatableChecksReadService;
         this.columnValidator = columnValidator;
+        this.savingAccountMapperForInterestPosting = new SavingAccountMapperForInterestPosting();
+        this.savingAccountAssembler = savingAccountAssembler;
     }
 
     @Override
@@ -230,6 +246,495 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
         }
     }
 
+    @Override
+    public List<SavingsAccountTransactionData> retrieveAllTransactionData(final List<String> refNo) throws DataAccessException {
+        String inSql = String.join(",", Collections.nCopies(refNo.size(), "?"));
+        String sql = "select " + this.savingsAccountTransactionsForBatchMapper.schema() + " where tr.ref_no in (%s)";
+        Object[] params = new Object[refNo.size()];
+        int i = 0;
+        for (String element : refNo) {
+            params[i] = element;
+            i++;
+        }
+        return this.jdbcTemplate.query(String.format(sql, inSql), this.savingsAccountTransactionsForBatchMapper, params);
+    }
+
+    @Override
+    public List<SavingsAccountData> retrieveAllSavingsDataForInterestPosting(final boolean backdatedTxnsAllowedTill, final int pageSize,
+            final Integer status, final Long maxSavingsId) {
+        LocalDate currentDate = DateUtils.getLocalDateOfTenant().minusDays(1);
+
+        String sql = "select " + this.savingAccountMapperForInterestPosting.schema()
+                + "join (select a.id from m_savings_account a where a.id > ? and a.status_enum = ? limit ?) b on b.id = sa.id ";
+        if (backdatedTxnsAllowedTill) {
+            sql = sql
+                    + "where if (sa.interest_posted_till_date is not null, tr.transaction_date >= sa.interest_posted_till_date, tr.transaction_date >= sa.activatedon_date) ";
+        }
+
+        sql = sql + "and apm.product_type=2 and sa.interest_posted_till_date<" + java.sql.Date.valueOf(currentDate);
+        sql = sql + " order by sa.id, tr.transaction_date, tr.created_date, tr.id";
+
+        List<SavingsAccountData> savingsAccountDataList = this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting,
+                new Object[] { maxSavingsId, status, pageSize });
+        for (SavingsAccountData savingsAccountData : savingsAccountDataList) {
+            this.savingAccountAssembler.assembleSavings(savingsAccountData);
+        }
+        return savingsAccountDataList;
+    }
+
+    private static final class SavingAccountMapperForInterestPosting implements ResultSetExtractor<List<SavingsAccountData>> {
+
+        private final String schemaSql;
+
+        SavingAccountMapperForInterestPosting() {
+            final StringBuilder sqlBuilder = new StringBuilder(400);
+            sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, ");
+            sqlBuilder.append("sa.deposit_type_enum as depositType, ");
+            sqlBuilder.append("c.id as clientId, c.office_id as clientOfficeId, ");
+            sqlBuilder.append("g.id as groupId, g.office_id as groupOfficeId, ");
+            sqlBuilder.append("sa.status_enum as statusEnum, ");
+            sqlBuilder.append("sa.sub_status_enum as subStatusEnum, ");
+            sqlBuilder.append("sa.submittedon_date as submittedOnDate,");
+
+            sqlBuilder.append("sa.rejectedon_date as rejectedOnDate,");
+
+            sqlBuilder.append("sa.withdrawnon_date as withdrawnOnDate,");
+
+            sqlBuilder.append("sa.approvedon_date as approvedOnDate,");
+
+            sqlBuilder.append("sa.activatedon_date as activatedOnDate,");
+
+            sqlBuilder.append("sa.closedon_date as closedOnDate,");
+
+            sqlBuilder.append(
+                    "sa.currency_code as currencyCode, sa.currency_digits as currencyDigits, sa.currency_multiplesof as inMultiplesOf, ");
+
+            sqlBuilder.append("sa.nominal_annual_interest_rate as nominalAnnualInterestRate, ");
+            sqlBuilder.append("sa.interest_compounding_period_enum as interestCompoundingPeriodType, ");
+            sqlBuilder.append("sa.interest_posting_period_enum as interestPostingPeriodType, ");
+            sqlBuilder.append("sa.interest_calculation_type_enum as interestCalculationType, ");
+            sqlBuilder.append("sa.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
+            sqlBuilder.append("sa.min_required_opening_balance as minRequiredOpeningBalance, ");
+            sqlBuilder.append("sa.lockin_period_frequency as lockinPeriodFrequency,");
+            sqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
+            sqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
+            sqlBuilder.append("sa.allow_overdraft as allowOverdraft, ");
+            sqlBuilder.append("sa.overdraft_limit as overdraftLimit, ");
+            sqlBuilder.append("sa.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
+            sqlBuilder.append("sa.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
+            sqlBuilder.append("sa.total_deposits_derived as totalDeposits, ");
+            sqlBuilder.append("sa.total_withdrawals_derived as totalWithdrawals, ");
+            sqlBuilder.append("sa.total_withdrawal_fees_derived as totalWithdrawalFees, ");
+            sqlBuilder.append("sa.total_annual_fees_derived as totalAnnualFees, ");
+            sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, ");
+            sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, ");
+            sqlBuilder.append("sa.total_overdraft_interest_derived as totalOverdraftInterestDerived, ");
+            sqlBuilder.append("sa.account_balance_derived as accountBalance, ");
+            sqlBuilder.append("sa.total_fees_charge_derived as totalFeeCharge, ");
+            sqlBuilder.append("sa.total_penalty_charge_derived as totalPenaltyCharge, ");
+            sqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
+            sqlBuilder.append("sa.min_required_balance as minRequiredBalance, ");
+            sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance, ");
+            sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds, ");
+            sqlBuilder.append("sa.withhold_tax as withHoldTax, ");
+            sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, ");
+            sqlBuilder.append("sa.last_interest_calculation_date as lastInterestCalculationDate, ");
+            sqlBuilder.append("sa.total_savings_amount_on_hold as onHoldAmount, ");
+            sqlBuilder.append("sa.interest_posted_till_date as interestPostedTillDate, ");
+            sqlBuilder.append("tg.id as taxGroupId, ");
+            sqlBuilder.append("(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
+            sqlBuilder.append("from m_savings_account_transaction as sat ");
+            sqlBuilder.append("where sat.is_reversed = 0 ");
+            sqlBuilder.append("and sat.transaction_type_enum in (1,2) ");
+            sqlBuilder.append("and sat.savings_account_id = sa.id) as lastActiveTransactionDate, ");
+            sqlBuilder.append("sp.id as productId, ");
+            sqlBuilder.append("sp.is_dormancy_tracking_active as isDormancyTrackingActive, ");
+            sqlBuilder.append("sp.days_to_inactive as daysToInactive, ");
+            sqlBuilder.append("sp.days_to_dormancy as daysToDormancy, ");
+            sqlBuilder.append("sp.days_to_escheat as daysToEscheat, ");
+            sqlBuilder.append("sp.accounting_type as accountingType, ");
+            sqlBuilder.append("tr.id as transactionId, tr.transaction_type_enum as transactionType, ");
+            sqlBuilder.append("tr.transaction_date as transactionDate, tr.amount as transactionAmount,");
+            sqlBuilder.append("tr.created_date as createdDate,tr.cumulative_balance_derived as cumulativeBalance,");
+            sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,");
+            sqlBuilder.append("tr.balance_end_date_derived as balanceEndDate, tr.overdraft_amount_derived as overdraftAmount,");
+            sqlBuilder.append("tr.is_manual as manualTransaction,tr.office_id as officeId, ");
+            sqlBuilder.append("pd.payment_type_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, ");
+            sqlBuilder.append("pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, ");
+            sqlBuilder.append("pt.value as paymentTypeName, ");
+            sqlBuilder.append("msacpb.amount as paidByAmount, msacpb.id as chargesPaidById, ");
+            sqlBuilder.append(
+                    "msac.id as chargeId, msac.amount as chargeAmount, msac.charge_time_enum as chargeTimeType, msac.is_penalty as isPenaltyCharge, ");
+            sqlBuilder.append("txd.id as taxDetailsId, txd.amount as taxAmount, ");
+            sqlBuilder.append("apm.gl_account_id as glAccountIdForInterestOnSavings, apm1.gl_account_id as glAccountIdForSavingsControl, ");
+            sqlBuilder.append(
+                    "mtc.id as taxComponentId, mtc.debit_account_id as debitAccountId, mtc.credit_account_id as creditAccountId, mtc.percentage as taxPercentage ");
+            sqlBuilder.append("from m_savings_account sa ");
+            sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
+            sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
+            sqlBuilder.append("join m_savings_account_transaction tr on sa.id = tr.savings_account_id ");
+            sqlBuilder.append("left join m_payment_detail pd on pd.id = tr.payment_detail_id ");
+            sqlBuilder.append("left join m_payment_type pt on pt.id = pd.payment_type_id ");
+            sqlBuilder.append("left join m_savings_account_charge_paid_by msacpb on msacpb.savings_account_transaction_id = tr.id ");
+            sqlBuilder.append("left join m_savings_account_charge msac on msac.id = msacpb.savings_account_charge_id ");
+            sqlBuilder.append("left join m_client c ON c.id = sa.client_id ");
+            sqlBuilder.append("left join m_group g ON g.id = sa.group_id ");
+            sqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id ");
+            sqlBuilder.append("left join m_savings_account_transaction_tax_details txd on txd.savings_transaction_id = tr.id ");
+            sqlBuilder.append("left join m_tax_component mtc on mtc.id = txd.tax_component_id ");
+            sqlBuilder.append("left join acc_product_mapping apm on apm.product_id = sp.id and apm.financial_account_type=3 ");
+            sqlBuilder.append("left join acc_product_mapping apm1 on apm1.product_id = sp.id and apm1.financial_account_type=2 ");
+
+            this.schemaSql = sqlBuilder.toString();
+        }
+
+        public String schema() {
+            return this.schemaSql;
+        }
+
+        @Override
+        public List<SavingsAccountData> extractData(final ResultSet rs) throws SQLException {
+
+            List<SavingsAccountData> savingsAccountDataList = new ArrayList<>();
+            HashMap<String, Long> savingsMap = new HashMap<>();
+            String currencyCode = null;
+            Integer currencyDigits = null;
+            Integer inMultiplesOf = null;
+            CurrencyData currency = null;
+            HashMap<String, Long> transMap = new HashMap<>();
+            HashMap<String, Long> taxDetails = new HashMap<>();
+            HashMap<String, Long> chargeDetails = new HashMap<>();
+            SavingsAccountTransactionData savingsAccountTransactionData = null;
+            SavingsAccountData savingsAccountData = null;
+            int count = 0;
+
+            while (rs.next()) {
+                final Long id = rs.getLong("id");
+                final Long transactionId = rs.getLong("transactionId");
+                final Long taxDetailId = JdbcSupport.getLongDefaultToNullIfZero(rs, "taxDetailsId");
+                final Long taxComponentId = JdbcSupport.getLongDefaultToNullIfZero(rs, "taxComponentId");
+                final String accountNo = rs.getString("accountNo");
+                final Long chargeId = rs.getLong("chargeId");
+
+                if (!savingsMap.containsValue(id)) {
+                    if (count > 0) {
+                        savingsAccountDataList.add(savingsAccountData);
+                    }
+                    count++;
+                    savingsMap.put("id", id);
+
+                    final String externalId = rs.getString("externalId");
+                    final Integer depositTypeId = rs.getInt("depositType");
+                    final EnumOptionData depositType = SavingsEnumerations.depositType(depositTypeId);
+                    final Long groupId = JdbcSupport.getLong(rs, "groupId");
+                    final Long groupOfficeId = JdbcSupport.getLong(rs, "groupOfficeId");
+                    final GroupGeneralData groupGeneralData = new GroupGeneralData(groupId, groupOfficeId);
+
+                    final Long clientId = JdbcSupport.getLong(rs, "clientId");
+                    final Long clientOfficeId = JdbcSupport.getLong(rs, "clientOfficeId");
+                    final ClientData clientData = ClientData.createClientForInterestPosting(clientId, clientOfficeId);
+
+                    final Long glAccountIdForInterestOnSavings = rs.getLong("glAccountIdForInterestOnSavings");
+                    final Long glAccountIdForSavingsControl = rs.getLong("glAccountIdForSavingsControl");
+
+                    final Long productId = rs.getLong("productId");
+                    final Integer accountType = rs.getInt("accountingType");
+                    final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountType);
+                    final EnumOptionData enumOptionDataForAccounting = new EnumOptionData(accountType.longValue(),
+                            accountingRuleType.getCode(), accountingRuleType.getValue().toString());
+                    final SavingsProductData savingsProductData = SavingsProductData.createForInterestPosting(productId,
+                            enumOptionDataForAccounting);
+
+                    final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum");
+                    final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusEnum);
+                    final Integer subStatusEnum = JdbcSupport.getInteger(rs, "subStatusEnum");
+                    final SavingsAccountSubStatusEnumData subStatus = SavingsEnumerations.subStatus(subStatusEnum);
+                    final LocalDate lastActiveTransactionDate = JdbcSupport.getLocalDate(rs, "lastActiveTransactionDate");
+                    final boolean isDormancyTrackingActive = rs.getBoolean("isDormancyTrackingActive");
+                    final Integer numDaysToInactive = JdbcSupport.getInteger(rs, "daysToInactive");
+                    final Integer numDaysToDormancy = JdbcSupport.getInteger(rs, "daysToDormancy");
+                    final Integer numDaysToEscheat = JdbcSupport.getInteger(rs, "daysToEscheat");
+                    Integer daysToInactive = null;
+                    Integer daysToDormancy = null;
+                    Integer daysToEscheat = null;
+
+                    LocalDate localTenantDate = DateUtils.getLocalDateOfTenant();
+                    if (isDormancyTrackingActive && statusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())) {
+                        if (subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()) {
+                            daysToEscheat = Math.toIntExact(
+                                    ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToEscheat)));
+                        }
+                        if (subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()) {
+                            daysToDormancy = Math.toIntExact(
+                                    ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToDormancy)));
+                        }
+                        if (subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()) {
+                            daysToInactive = Math.toIntExact(
+                                    ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToInactive)));
+                        }
+                    }
+                    final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate");
+                    final LocalDate withdrawnOnDate = JdbcSupport.getLocalDate(rs, "withdrawnOnDate");
+                    final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
+                    final LocalDate activatedOnDate = JdbcSupport.getLocalDate(rs, "activatedOnDate");
+                    final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate");
+                    final SavingsAccountApplicationTimelineData timeline = new SavingsAccountApplicationTimelineData(submittedOnDate, null,
+                            null, null, null, null, null, null, withdrawnOnDate, null, null, null, approvedOnDate, null, null, null,
+                            activatedOnDate, null, null, null, closedOnDate, null, null, null);
+
+                    currencyCode = rs.getString("currencyCode");
+                    currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
+                    inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
+                    currency = new CurrencyData(currencyCode, currencyDigits, inMultiplesOf);
+
+                    final BigDecimal totalDeposits = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalDeposits");
+                    final BigDecimal totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals");
+                    final BigDecimal totalWithdrawalFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawalFees");
+                    final BigDecimal totalAnnualFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalAnnualFees");
+
+                    final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned");
+                    final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalInterestPosted");
+                    final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
+                    final BigDecimal totalFeeCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge");
+                    final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge");
+                    final BigDecimal totalOverdraftInterestDerived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs,
+                            "totalOverdraftInterestDerived");
+                    final BigDecimal totalWithholdTax = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithholdTax");
+                    final LocalDate interestPostedTillDate = JdbcSupport.getLocalDate(rs, "interestPostedTillDate");
+
+                    final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                            "minBalanceForInterestCalculation");
+                    final BigDecimal onHoldFunds = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "onHoldFunds");
+
+                    final BigDecimal onHoldAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "onHoldAmount");
+
+                    BigDecimal availableBalance = accountBalance;
+                    if (availableBalance != null && onHoldFunds != null) {
+
+                        availableBalance = availableBalance.subtract(onHoldFunds);
+                    }
+
+                    if (availableBalance != null && onHoldAmount != null) {
+
+                        availableBalance = availableBalance.subtract(onHoldAmount);
+                    }
+
+                    BigDecimal interestNotPosted = BigDecimal.ZERO;
+                    LocalDate lastInterestCalculationDate = null;
+                    if (totalInterestEarned != null) {
+                        interestNotPosted = totalInterestEarned.subtract(totalInterestPosted).add(totalOverdraftInterestDerived);
+                        lastInterestCalculationDate = JdbcSupport.getLocalDate(rs, "lastInterestCalculationDate");
+                    }
+
+                    final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
+                            totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge,
+                            totalPenaltyCharge, totalOverdraftInterestDerived, totalWithholdTax, interestNotPosted,
+                            lastInterestCalculationDate, availableBalance, interestPostedTillDate);
+                    summary.setPrevInterestPostedTillDate(interestPostedTillDate);
+
+                    final boolean withHoldTax = rs.getBoolean("withHoldTax");
+                    final Long taxGroupId = JdbcSupport.getLongDefaultToNullIfZero(rs, "taxGroupId");
+                    TaxGroupData taxGroupData = null;
+                    if (taxGroupId != null) {
+                        taxGroupData = TaxGroupData.lookup(taxGroupId, null);
+                    }
+
+                    final BigDecimal nominalAnnualInterestRate = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                            "nominalAnnualInterestRate");
+
+                    final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations.compoundingInterestPeriodType(
+                            SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestCompoundingPeriodType")));
+
+                    final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(
+                            SavingsPostingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
+
+                    final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(
+                            SavingsInterestCalculationType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
+
+                    final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations
+                            .interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType
+                                    .fromInt(JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType")));
+
+                    final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                            "minRequiredOpeningBalance");
+
+                    final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
+                    EnumOptionData lockinPeriodFrequencyType = null;
+                    final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
+                    if (lockinPeriodFrequencyTypeValue != null) {
+                        final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType
+                                .fromInt(lockinPeriodFrequencyTypeValue);
+                        lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType);
+                    }
+
+                    final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
+
+                    final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
+                    final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
+                    final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                            "nominalAnnualInterestRateOverdraft");
+                    final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
+                            "minOverdraftForInterestCalculation");
+
+                    final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
+                    final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
+                    savingsAccountData = SavingsAccountData.instance(id, accountNo, depositType, externalId, null, null, null, null,
+                            productId, null, null, null, status, subStatus, timeline, currency, nominalAnnualInterestRate,
+                            interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
+                            interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
+                            withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance,
+                            enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
+                            minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate,
+                            isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
+
+                    savingsAccountData.setClientData(clientData);
+                    savingsAccountData.setGroupGeneralData(groupGeneralData);
+                    savingsAccountData.setSavingsProduct(savingsProductData);
+                    savingsAccountData.setGlAccountIdForInterestOnSavings(glAccountIdForInterestOnSavings);
+                    savingsAccountData.setGlAccountIdForSavingsControl(glAccountIdForSavingsControl);
+                }
+
+                if (!transMap.containsValue(transactionId)) {
+
+                    final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType");
+                    final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations.transactionType(transactionTypeInt);
+
+                    final LocalDate date = JdbcSupport.getLocalDate(rs, "transactionDate");
+                    final LocalDate balanceEndDate = JdbcSupport.getLocalDate(rs, "balanceEndDate");
+                    final LocalDate transSubmittedOnDate = JdbcSupport.getLocalDate(rs, "createdDate");
+                    final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transactionAmount");
+                    final BigDecimal overdraftAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "overdraftAmount");
+                    final BigDecimal outstandingChargeAmount = null;
+                    final BigDecimal runningBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "runningBalance");
+                    final boolean reversed = rs.getBoolean("reversed");
+                    final boolean isManualTransaction = rs.getBoolean("manualTransaction");
+                    final Long officeId = rs.getLong("officeId");
+                    final BigDecimal cumulativeBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "cumulativeBalance");
+
+                    final boolean postInterestAsOn = false;
+
+                    PaymentDetailData paymentDetailData = null;
+                    if (transactionType.isDepositOrWithdrawal()) {
+                        final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentType");
+                        if (paymentTypeId != null) {
+                            final String typeName = rs.getString("paymentTypeName");
+                            final PaymentTypeData paymentTypeData = new PaymentTypeData(paymentTypeId, typeName, null, false, null);
+                            paymentDetailData = new PaymentDetailData(id, paymentTypeData, null, null, null, null, null);
+                        }
+                    }
+
+                    savingsAccountTransactionData = SavingsAccountTransactionData.create(transactionId, transactionType, paymentDetailData,
+                            id, accountNo, date, currency, amount, outstandingChargeAmount, runningBalance, reversed, transSubmittedOnDate,
+                            postInterestAsOn, cumulativeBalance, balanceEndDate);
+                    savingsAccountTransactionData.setOverdraftAmount(overdraftAmount);
+
+                    transMap.put("id", transactionId);
+                    if (savingsAccountData.getOfficeId() == null) {
+                        savingsAccountData.setOfficeId(officeId);
+                    }
+
+                    savingsAccountData.setSavingsAccountTransactionData(savingsAccountTransactionData);
+                }
+
+                if (chargeId != null && !chargeDetails.containsValue(chargeId)) {
+                    final boolean isPenalty = rs.getBoolean("isPenaltyCharge");
+                    final BigDecimal chargeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "chargeAmount");
+                    final Integer chargesTimeType = rs.getInt("chargeTimeType");
+                    final EnumOptionData enumOptionDataForChargesTimeType = new EnumOptionData(chargesTimeType.longValue(), null, null);
+                    final SavingsAccountChargeData savingsAccountChargeData = new SavingsAccountChargeData(chargeId, chargeAmount,
+                            enumOptionDataForChargesTimeType, isPenalty);
+
+                    final Long chargesPaidById = rs.getLong("chargesPaidById");
+                    final BigDecimal chargesPaid = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "paidByAmount");
+                    final SavingsAccountChargesPaidByData savingsAccountChargesPaidByData = new SavingsAccountChargesPaidByData(
+                            chargesPaidById, chargesPaid);
+                    savingsAccountChargesPaidByData.setSavingsAccountChargeData(savingsAccountChargeData);
+                    if (savingsAccountChargesPaidByData != null) {
+                        savingsAccountTransactionData.setChargesPaidByData(savingsAccountChargesPaidByData);
+                    }
+
+                    chargeDetails.put("id", chargeId);
+                }
+
+                if (taxDetailId != null && !taxDetails.containsValue(taxDetailId)) {
+                    final BigDecimal amount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "taxAmount");
+                    final BigDecimal percentage = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "taxPercentage");
+                    final Long debitId = rs.getLong("debitAccountId");
+                    final Long creditId = rs.getLong("creditAccountId");
+                    final GLAccountData debitAccount = GLAccountData.createFrom(debitId);
+                    final GLAccountData creditAccount = GLAccountData.createFrom(creditId);
+
+                    if (taxComponentId != null) {
+                        final TaxComponentData taxComponent = TaxComponentData.createTaxComponent(taxComponentId, percentage, debitAccount,
+                                creditAccount);
+                        savingsAccountTransactionData.setTaxDetails(new TaxDetailsData(taxComponent, amount));
+                    }
+
+                    taxDetails.put("id", taxDetailId);
+                }
+
+            }
+            if (savingsAccountData != null) {
+                savingsAccountDataList.add(savingsAccountData);
+            }
+            return savingsAccountDataList;
+
+            // final String productName = rs.getString("productName");
+
+            // final String fieldOfficerName = rs.getString("fieldOfficerName");
+
+            // final String submittedByUsername = rs.getString("submittedByUsername");
+            // final String submittedByFirstname = rs.getString("submittedByFirstname");
+            // final String submittedByLastname = rs.getString("submittedByLastname");
+            //
+
+            // final String rejectedByUsername = rs.getString("rejectedByUsername");
+            // final String rejectedByFirstname = rs.getString("rejectedByFirstname");
+            // final String rejectedByLastname = rs.getString("rejectedByLastname");
+            //
+
+            // final String withdrawnByUsername = rs.getString("withdrawnByUsername");
+            // final String withdrawnByFirstname = rs.getString("withdrawnByFirstname");
+            // final String withdrawnByLastname = rs.getString("withdrawnByLastname");
+
+            // final String approvedByUsername = rs.getString("approvedByUsername");
+            // final String approvedByFirstname = rs.getString("approvedByFirstname");
+            // final String approvedByLastname = rs.getString("approvedByLastname");
+
+            // final String activatedByUsername = rs.getString("activatedByUsername");
+            // final String activatedByFirstname = rs.getString("activatedByFirstname");
+            // final String activatedByLastname = rs.getString("activatedByLastname");
+
+            // final String closedByUsername = rs.getString("closedByUsername");
+            // final String closedByFirstname = rs.getString("closedByFirstname");
+            // final String closedByLastname = rs.getString("closedByLastname");
+
+            // final String currencyName = rs.getString("currencyName");
+            // final String currencyNameCode = rs.getString("currencyNameCode");
+            // final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
+
+            /*
+             * final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount");
+             *
+             * EnumOptionData withdrawalFeeType = null; final Integer withdrawalFeeTypeValue =
+             * JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); if (withdrawalFeeTypeValue != null) {
+             * withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); }
+             */
+
+            /*
+             * final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount");
+             *
+             * MonthDay annualFeeOnMonthDay = null; final Integer annualFeeOnMonth = JdbcSupport.getInteger(rs,
+             * "annualFeeOnMonth"); final Integer annualFeeOnDay = JdbcSupport.getInteger(rs, "annualFeeOnDay"); if
+             * (annualFeeAmount != null && annualFeeOnDay != null) { annualFeeOnMonthDay = new
+             * MonthDay(annualFeeOnMonth, annualFeeOnDay); }
+             *
+             * final LocalDate annualFeeNextDueDate = JdbcSupport.getLocalDate(rs, "annualFeeNextDueDate");
+             */
+
+        }
+    }
+
     private static final class SavingAccountMapper implements RowMapper<SavingsAccountData> {
 
         private final String schemaSql;
@@ -285,7 +790,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
             // withdrawalFeeAmount,");
             // sqlBuilder.append("sa.withdrawal_fee_type_enum as
             // withdrawalFeeTypeEnum, ");
-            sqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
             sqlBuilder.append("sa.allow_overdraft as allowOverdraft, ");
             sqlBuilder.append("sa.overdraft_limit as overdraftLimit, ");
             sqlBuilder.append("sa.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
@@ -315,6 +819,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
             sqlBuilder.append("sa.last_interest_calculation_date as lastInterestCalculationDate, ");
             sqlBuilder.append("sa.interest_posted_till_date as interestPostedTillDate, ");
             sqlBuilder.append("sa.total_savings_amount_on_hold as onHoldAmount, ");
+            sqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
             sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
             sqlBuilder.append("(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
             sqlBuilder.append("from m_savings_account_transaction as sat ");
@@ -337,7 +842,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
             sqlBuilder.append("left join m_appuser abu on abu.id = sa.approvedon_userid ");
             sqlBuilder.append("left join m_appuser avbu on avbu.id = sa.activatedon_userid ");
             sqlBuilder.append("left join m_appuser cbu on cbu.id = sa.closedon_userid ");
-            sqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id  ");
+            sqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id ");
 
             this.schemaSql = sqlBuilder.toString();
         }
@@ -357,6 +862,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
 
             final Long groupId = JdbcSupport.getLong(rs, "groupId");
             final String groupName = rs.getString("groupName");
+
             final Long clientId = JdbcSupport.getLong(rs, "clientId");
             final String clientName = rs.getString("clientName");
 
@@ -765,6 +1271,33 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
                 new Object[] { savingsId, depositAccountType.getValue(), transactionId });
     }
 
+    private static final class SavingsAccountTransactionsForBatchMapper implements RowMapper<SavingsAccountTransactionData> {
+
+        private final String schemaSql;
+
+        SavingsAccountTransactionsForBatchMapper() {
+
+            final StringBuilder sqlBuilder = new StringBuilder(400);
+            sqlBuilder.append("tr.id as transactionId, tr.ref_no as refNo ");
+            sqlBuilder.append("from m_savings_account_transaction tr");
+
+            this.schemaSql = sqlBuilder.toString();
+        }
+
+        public String schema() {
+            return this.schemaSql;
+        }
+
+        @Override
+        public SavingsAccountTransactionData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
+            final Long id = rs.getLong("transactionId");
+            SavingsAccountTransactionData savingsAccountTransactionData = SavingsAccountTransactionData.create(id);
+            final String refNo = rs.getString("refNo");
+            savingsAccountTransactionData.setRefNo(refNo);
+            return savingsAccountTransactionData;
+        }
+    }
+
     /*
      * @Override public Collection<SavingsAccountAnnualFeeData> retrieveAccountsWithAnnualFeeDue() { final String sql =
      * "select " + this.annualFeeMapper.schema() +
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
index b72d60c..134460e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
@@ -25,6 +25,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.organisation.office.domain.Office;
 import org.apache.fineract.organisation.staff.domain.Staff;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
 
@@ -87,6 +88,12 @@ public interface SavingsAccountWritePlatformService {
 
     void postInterest(SavingsAccount account, boolean postInterestAs, LocalDate transactionDate, boolean backdatedTxnsAllowedTill);
 
+    // SavingsAccountData postInterest(SavingsAccountData account, boolean postInterestAs, LocalDate transactionDate,
+    // boolean backdatedTxnsAllowedTill);
+
+    SavingsAccountData postInterest(SavingsAccountData account, boolean postInterestAs, LocalDate transactionDate,
+            boolean backdatedTxnsAllowedTill);
+
     CommandProcessingResult blockAccount(Long savingsId);
 
     CommandProcessingResult unblockAccount(Long savingsId);
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 712ed12..eac3a8a 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
@@ -55,6 +55,7 @@ import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRu
 import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
 import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
 import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
 import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
@@ -93,8 +94,10 @@ import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
 import org.apache.fineract.portfolio.savings.SavingsApiConstants;
 import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeDataValidator;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountDataValidator;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDataValidator;
 import org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransaction;
 import org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository;
@@ -123,6 +126,7 @@ import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
@@ -156,6 +160,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
     private final StandingInstructionRepository standingInstructionRepository;
     private final BusinessEventNotifierService businessEventNotifierService;
     private final GSIMRepositoy gsimRepository;
+    private final JdbcTemplate jdbcTemplate;
+    private final SavingsAccountInterestPostingService savingsAccountInterestPostingService;
 
     @Autowired
     public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -177,7 +183,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
             final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository,
             final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService,
             final AppUserRepositoryWrapper appuserRepository, final StandingInstructionRepository standingInstructionRepository,
-            final BusinessEventNotifierService businessEventNotifierService, final GSIMRepositoy gsimRepository) {
+            final BusinessEventNotifierService businessEventNotifierService, final GSIMRepositoy gsimRepository,
+            final RoutingDataSource dataSource, final SavingsAccountInterestPostingService savingsAccountInterestPostingService) {
         this.context = context;
         this.savingAccountRepositoryWrapper = savingAccountRepositoryWrapper;
         this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
@@ -204,6 +211,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         this.standingInstructionRepository = standingInstructionRepository;
         this.businessEventNotifierService = businessEventNotifierService;
         this.gsimRepository = gsimRepository;
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+        this.savingsAccountInterestPostingService = savingsAccountInterestPostingService;
     }
 
     private static final Logger LOG = LoggerFactory.getLogger(SavingsAccountWritePlatformServiceJpaRepositoryImpl.class);
@@ -582,6 +591,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
                 .isSavingsInterestPostingAtCurrentPeriodEnd();
         final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
+
         if (account.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) > 0
                 || (account.allowOverdraft() && account.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO) > 0)) {
             final Set<Long> existingTransactionIds = new HashSet<>();
@@ -623,6 +633,52 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         }
     }
 
+    @Transactional
+    @Override
+    public SavingsAccountData postInterest(SavingsAccountData savingsAccountData, final boolean postInterestAs,
+            final LocalDate transactionDate, final boolean backdatedTxnsAllowedTill) {
+
+        final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
+                .isSavingsInterestPostingAtCurrentPeriodEnd();
+        final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
+
+        if (savingsAccountData.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) > 0 || (savingsAccountData.isAllowOverdraft()
+                && savingsAccountData.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO) > 0)) {
+            final Set<Long> existingTransactionIds = new HashSet<>();
+            final Set<Long> existingReversedTransactionIds = new HashSet<>();
+            updateExistingTransactionsDetails(savingsAccountData, existingTransactionIds, existingReversedTransactionIds);
+
+            final LocalDate today = DateUtils.getLocalDateOfTenant();
+            final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode());
+            boolean isInterestTransfer = false;
+            LocalDate postInterestOnDate = null;
+            if (postInterestAs) {
+                postInterestOnDate = transactionDate;
+            }
+
+            long startPosting = System.currentTimeMillis();
+            LOG.info("Interest Posting Start Here at {}", startPosting);
+
+            savingsAccountData = this.savingsAccountInterestPostingService.postInterest(mc, today, isInterestTransfer,
+                    isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill,
+                    savingsAccountData);
+            long endPosting = System.currentTimeMillis();
+            LOG.info("Interest Posting Ends within {}", endPosting - startPosting);
+
+            if (!backdatedTxnsAllowedTill) {
+                List<SavingsAccountTransactionData> transactions = savingsAccountData.getTransactions();
+                for (SavingsAccountTransactionData accountTransaction : transactions) {
+                    if (accountTransaction.getId() == null) {
+                        savingsAccountData.setNewSavingsAccountTransactionData(accountTransaction);
+                    }
+                }
+            }
+            savingsAccountData.setExistingTransactionIds(existingTransactionIds);
+            savingsAccountData.setExistingReversedTransactionIds(existingReversedTransactionIds);
+        }
+        return savingsAccountData;
+    }
+
     @Override
     public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId,
             final boolean allowAccountTransferModification) {
@@ -630,7 +686,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
                 .isSavingsInterestPostingAtCurrentPeriodEnd();
         final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
-
         final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, false);
         final Set<Long> existingTransactionIds = new HashSet<>();
         final Set<Long> existingReversedTransactionIds = new HashSet<>();
@@ -1394,12 +1449,25 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds());
     }
 
+    private void updateExistingTransactionsDetails(SavingsAccountData account, Set<Long> existingTransactionIds,
+            Set<Long> existingReversedTransactionIds) {
+        existingTransactionIds.addAll(account.findCurrentTransactionIdsWithPivotDateConfig());
+        existingReversedTransactionIds.addAll(account.findCurrentReversedTransactionIdsWithPivotDateConfig());
+    }
+
     private void updateSavingsTransactionsDetails(SavingsAccount account, Set<Long> existingTransactionIds,
             Set<Long> existingReversedTransactionIds) {
         existingTransactionIds.addAll(account.findCurrentTransactionIdsWithPivotDateConfig());
         existingReversedTransactionIds.addAll(account.findCurrentReversedTransactionIdsWithPivotDateConfig());
     }
 
+    @SuppressWarnings("unused")
+    private void updateSavingsTransactionsDetails(SavingsAccountData account, Set<Long> existingTransactionIds,
+            Set<Long> existingReversedTransactionIds) {
+        existingTransactionIds.addAll(account.findCurrentTransactionIdsWithPivotDateConfig());
+        existingReversedTransactionIds.addAll(account.findCurrentReversedTransactionIdsWithPivotDateConfig());
+    }
+
     private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
             final Set<Long> existingReversedTransactionIds, final boolean backdatedTxnsAllowedTill) {
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
index f5dde25..3e0097e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
@@ -19,25 +19,40 @@
 package org.apache.fineract.portfolio.savings.service;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.math.BigDecimal;
 import java.security.SecureRandom;
+import java.sql.Date;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.UUID;
 import java.util.concurrent.Callable;
+import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
+import org.apache.fineract.batch.command.CommandStrategyProvider;
+import org.apache.fineract.batch.service.ResolutionHelper;
 import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
 import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
-import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.annotation.Scope;
 import org.springframework.dao.CannotAcquireLockException;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.orm.ObjectOptimisticLockingFailureException;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.support.TransactionTemplate;
 
 /**
  * @author manoj
@@ -49,22 +64,38 @@ public class SavingsSchedularInterestPoster implements Callable<Void> {
 
     private static final Logger LOG = LoggerFactory.getLogger(SavingsSchedularInterestPoster.class);
     private static final SecureRandom random = new SecureRandom();
+    private static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
 
-    private Collection<Long> savingsIds;
+    private Collection<SavingsAccountData> savingAccounts;
     private SavingsAccountWritePlatformService savingsAccountWritePlatformService;
     private SavingsAccountRepositoryWrapper savingsAccountRepository;
     private SavingsAccountAssembler savingAccountAssembler;
     private FineractPlatformTenant tenant;
     private ConfigurationDomainService configurationDomainService;
+    private boolean backdatedTxnsAllowedTill;
+    private List<SavingsAccountData> savingsAccountDataList = new ArrayList<>();
+    private JdbcTemplate jdbcTemplate;
+    private TransactionTemplate transactionTemplate;
+    private CommandStrategyProvider strategyProvider;
+    private ResolutionHelper resolutionHelper;
+    private SavingsAccountReadPlatformService savingsAccountReadPlatformService;
 
-    public void setSavingsIds(Collection<Long> savingsIds) {
-        this.savingsIds = savingsIds;
+    public void setSavings(Collection<SavingsAccountData> savingAccounts) {
+        this.savingAccounts = savingAccounts;
+    }
+
+    public void setBackdatedTxnsAllowedTill(final boolean backdatedTxnsAllowedTill) {
+        this.backdatedTxnsAllowedTill = backdatedTxnsAllowedTill;
     }
 
     public void setSavingsAccountWritePlatformService(SavingsAccountWritePlatformService savingsAccountWritePlatformService) {
         this.savingsAccountWritePlatformService = savingsAccountWritePlatformService;
     }
 
+    public void setSavingsAccountReadPlatformService(SavingsAccountReadPlatformService savingsAccountReadPlatformService) {
+        this.savingsAccountReadPlatformService = savingsAccountReadPlatformService;
+    }
+
     public void setSavingsAccountRepository(SavingsAccountRepositoryWrapper savingsAccountRepository) {
         this.savingsAccountRepository = savingsAccountRepository;
     }
@@ -77,6 +108,22 @@ public class SavingsSchedularInterestPoster implements Callable<Void> {
         this.tenant = tenant;
     }
 
+    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
+        this.transactionTemplate = transactionTemplate;
+    }
+
+    public void setResolutionHelper(ResolutionHelper resolutionHelper) {
+        this.resolutionHelper = resolutionHelper;
+    }
+
+    public void setStrategyProvider(CommandStrategyProvider commandStrategyProvider) {
+        this.strategyProvider = commandStrategyProvider;
+    }
+
     @Override
     @SuppressFBWarnings(value = {
             "DMI_RANDOM_USED_ONLY_ONCE" }, justification = "False positive for random object created and used only once")
@@ -84,31 +131,38 @@ public class SavingsSchedularInterestPoster implements Callable<Void> {
         ThreadLocalContextUtil.setTenant(tenant);
         Integer maxNumberOfRetries = tenant.getConnection().getMaxRetriesOnDeadlock();
         Integer maxIntervalBetweenRetries = tenant.getConnection().getMaxIntervalBetweenRetries();
-        final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig();
+
+        // List<BatchResponse> responseList = new ArrayList<>();
+        long start = System.currentTimeMillis();
+        LOG.info("Thread Execution Started at {}", start);
+
+        List<Throwable> errors = new ArrayList<>();
         int i = 0;
-        if (!savingsIds.isEmpty()) {
-            List<Throwable> errors = new ArrayList<>();
-            for (Long savingsId : savingsIds) {
-                LOG.info("Savings ID {}", savingsId);
+        if (!savingAccounts.isEmpty()) {
+            for (SavingsAccountData savingsAccountData : savingAccounts) {
                 Integer numberOfRetries = 0;
                 while (numberOfRetries <= maxNumberOfRetries) {
                     try {
-                        SavingsAccount savingsAccount = this.savingsAccountRepository.findOneWithNotFoundDetection(savingsId);
-                        this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount);
                         boolean postInterestAsOn = false;
                         LocalDate transactionDate = null;
-                        this.savingsAccountWritePlatformService.postInterest(savingsAccount, postInterestAsOn, transactionDate,
-                                backdatedTxnsAllowedTill);
+                        long startPosting = System.currentTimeMillis();
+                        SavingsAccountData savingsAccountDataRet = savingsAccountWritePlatformService.postInterest(savingsAccountData,
+                                postInterestAsOn, transactionDate, backdatedTxnsAllowedTill);
+                        long endPosting = System.currentTimeMillis();
+                        savingsAccountDataList.add(savingsAccountDataRet);
+
+                        LOG.info("Posting Completed Within {}", endPosting - startPosting);
 
                         numberOfRetries = maxNumberOfRetries + 1;
                     } catch (CannotAcquireLockException | ObjectOptimisticLockingFailureException exception) {
-                        LOG.info("Interest posting job for savings ID {} has been retried {} time(s)", savingsId, numberOfRetries);
+                        LOG.info("Interest posting job for savings ID {} has been retried {} time(s)", savingsAccountData.getId(),
+                                numberOfRetries);
                         // Fail if the transaction has been retired for
                         // maxNumberOfRetries
                         if (numberOfRetries >= maxNumberOfRetries) {
                             LOG.error(
                                     "Interest posting job for savings ID {} has been retried for the max allowed attempts of {} and will be rolled back",
-                                    savingsId, numberOfRetries);
+                                    savingsAccountData.getId(), numberOfRetries);
                             errors.add(exception);
                             break;
                         }
@@ -124,18 +178,195 @@ public class SavingsSchedularInterestPoster implements Callable<Void> {
                             break;
                         }
                     } catch (Exception e) {
-                        LOG.error("Interest posting job for savings failed for account {}", savingsId, e);
+                        LOG.error("Interest posting job for savings failed for account {}", savingsAccountData.getId(), e);
                         numberOfRetries = maxNumberOfRetries + 1;
                         errors.add(e);
                     }
-                    i++;
                 }
-                LOG.info("Savings count {}", i);
+                i++;
+            }
+
+            if (errors.isEmpty()) {
+                try {
+                    batchUpdate(savingsAccountDataList);
+                } catch (DataAccessException exception) {
+                    LOG.error("Batch update failed due to DataAccessException", exception);
+                    errors.add(exception);
+                } catch (NullPointerException exception) {
+                    LOG.error("Batch update failed due to NullPointerException", exception);
+                    errors.add(exception);
+                }
             }
+
             if (!errors.isEmpty()) {
                 throw new JobExecutionException(errors);
             }
         }
+
+        long end = System.currentTimeMillis();
+        LOG.info("Time To Finish the batch {} by thread {} for accounts {}", end - start, Thread.currentThread().getId(),
+                savingAccounts.size());
         return null;
     }
+
+    @Transactional(isolation = Isolation.READ_UNCOMMITTED, rollbackFor = Exception.class)
+    private void batchUpdateJournalEntries(final List<SavingsAccountData> savingsAccountDataList,
+            final HashMap<String, SavingsAccountTransactionData> savingsAccountTransactionDataHashMap)
+            throws DataAccessException, NullPointerException {
+        String queryForJGLUpdate = batchQueryForJournalEntries();
+        List<Object[]> paramsForGLInsertion = new ArrayList<>();
+        for (SavingsAccountData savingsAccountData : savingsAccountDataList) {
+            String currencyCode = savingsAccountData.getCurrency().getCode();
+
+            List<SavingsAccountTransactionData> savingsAccountTransactionDataList = savingsAccountData.getSavingsAccountTransactionData();
+            for (SavingsAccountTransactionData savingsAccountTransactionData : savingsAccountTransactionDataList) {
+                if (savingsAccountTransactionData.getId() == null) {
+                    final String key = savingsAccountTransactionData.getRefNo();
+                    if (savingsAccountTransactionDataHashMap.containsKey(key)) {
+                        LocalDate currentDate = DateUtils.getLocalDateOfTenant();
+                        final SavingsAccountTransactionData dataFromFetch = savingsAccountTransactionDataHashMap.get(key);
+                        savingsAccountTransactionData.setId(dataFromFetch.getId());
+
+                        paramsForGLInsertion.add(
+                                new Object[] { savingsAccountData.getGlAccountIdForSavingsControl(), savingsAccountData.getOfficeId(), null,
+                                        currencyCode, SAVINGS_TRANSACTION_IDENTIFIER + savingsAccountTransactionData.getId().toString(),
+                                        savingsAccountTransactionData.getId(), null, false, null, false,
+                                        Date.from(savingsAccountTransactionData.getTransactionDate()
+                                                .atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()),
+                                        JournalEntryType.CREDIT.getValue().longValue(), savingsAccountTransactionData.getAmount(), null,
+                                        JournalEntryType.CREDIT.getValue().longValue(), savingsAccountData.getId(),
+                                        Date.from(currentDate.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()),
+                                        Date.from(currentDate.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()), false,
+                                        BigDecimal.ZERO, BigDecimal.ZERO, null,
+                                        Date.from(savingsAccountTransactionData.getTransactionDate()
+                                                .atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()),
+                                        null, Integer.valueOf(1), Integer.valueOf(1) });
+
+                        paramsForGLInsertion.add(new Object[] { savingsAccountData.getGlAccountIdForInterestOnSavings(),
+                                savingsAccountData.getOfficeId(), null, currencyCode,
+                                SAVINGS_TRANSACTION_IDENTIFIER + savingsAccountTransactionData.getId().toString(),
+                                savingsAccountTransactionData.getId(), null, false, null, false,
+                                Date.from(savingsAccountTransactionData.getTransactionDate()
+                                        .atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()),
+                                JournalEntryType.DEBIT.getValue().longValue(), savingsAccountTransactionData.getAmount(), null,
+                                JournalEntryType.DEBIT.getValue().longValue(), savingsAccountData.getId(),
+                                Date.from(currentDate.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()),
+                                Date.from(currentDate.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()), false,
+                                BigDecimal.ZERO, BigDecimal.ZERO, null,
+                                Date.from(savingsAccountTransactionData.getTransactionDate()
+                                        .atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()),
+                                null, Integer.valueOf(1), Integer.valueOf(1) });
+
+                    }
+                }
+            }
+        }
+
+        if (paramsForGLInsertion != null && paramsForGLInsertion.size() > 0) {
+            this.jdbcTemplate.batchUpdate(queryForJGLUpdate, paramsForGLInsertion);
+        }
+    }
+
+    private String batchQueryForJournalEntries() {
+        StringBuilder query = new StringBuilder(100);
+
+        query.append("INSERT INTO acc_gl_journal_entry(account_id,office_id,reversal_id,currency_code,transaction_id,");
+        query.append("savings_transaction_id,client_transaction_id,reversed,ref_num,manual_entry,entry_date,type_enum,");
+        query.append("amount,description,entity_type_enum,entity_id,created_date,");
+        query.append("lastmodified_date,is_running_balance_calculated,office_running_balance,organization_running_balance,");
+        query.append("payment_details_id,transaction_date,share_transaction_id, createdby_id, lastmodifiedby_id) ");
+        query.append("VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+
+        return query.toString();
+    }
+
+    private List<SavingsAccountTransactionData> fetchTransactionsFromIds(final List<String> refNo) throws DataAccessException {
+        return this.savingsAccountReadPlatformService.retrieveAllTransactionData(refNo);
+    }
+
+    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
+    @SuppressWarnings("unused")
+    private void batchUpdate(final List<SavingsAccountData> savingsAccountDataList) throws DataAccessException {
+        String queryForSavingsUpdate = batchQueryForSavingsSummaryUpdate();
+        String queryForTransactionInsertion = batchQueryForTransactionInsertion();
+        List<Object[]> paramsForTransactionInsertion = new ArrayList<>();
+        List<Object[]> paramsForSavingsSummary = new ArrayList<>();
+        List<String> transRefNo = new ArrayList<>();
+        for (SavingsAccountData savingsAccountData : savingsAccountDataList) {
+            SavingsAccountSummaryData savingsAccountSummaryData = savingsAccountData.getSummary();
+            paramsForSavingsSummary.add(new Object[] { savingsAccountSummaryData.getTotalDeposits(),
+                    savingsAccountSummaryData.getTotalWithdrawals(), savingsAccountSummaryData.getTotalInterestEarned(),
+                    savingsAccountSummaryData.getTotalInterestPosted(), savingsAccountSummaryData.getTotalWithdrawalFees(),
+                    savingsAccountSummaryData.getTotalFeeCharge(), savingsAccountSummaryData.getTotalPenaltyCharge(),
+                    savingsAccountSummaryData.getTotalAnnualFees(), savingsAccountSummaryData.getAvailableBalance(),
+                    savingsAccountSummaryData.getTotalOverdraftInterestDerived(), savingsAccountSummaryData.getTotalWithholdTax(),
+                    Date.from(savingsAccountSummaryData.getLastInterestCalculationDate().atStartOfDay(DateUtils.getDateTimeZoneOfTenant())
+                            .toInstant()),
+                    Date.from(savingsAccountSummaryData.getInterestPostedTillDate().atStartOfDay(DateUtils.getDateTimeZoneOfTenant())
+                            .toInstant()),
+                    savingsAccountData.getId() });
+            List<SavingsAccountTransactionData> savingsAccountTransactionDataList = savingsAccountData.getSavingsAccountTransactionData();
+            for (SavingsAccountTransactionData savingsAccountTransactionData : savingsAccountTransactionDataList) {
+                if (savingsAccountTransactionData.getId() == null) {
+                    UUID uuid = UUID.randomUUID();
+                    savingsAccountTransactionData.setRefNo(uuid.toString());
+                    transRefNo.add(uuid.toString());
+                    java.util.Date balanceEndDate = null;
+                    if (savingsAccountTransactionData.getBalanceEndDate() != null) {
+                        balanceEndDate = Date.from(savingsAccountTransactionData.getBalanceEndDate()
+                                .atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant());
+                    }
+                    paramsForTransactionInsertion.add(new Object[] { savingsAccountData.getId(), savingsAccountData.getOfficeId(),
+                            savingsAccountTransactionData.getTransactionType().getId(),
+                            Date.from(savingsAccountTransactionData.getTransactionDate().atStartOfDay(DateUtils.getDateTimeZoneOfTenant())
+                                    .toInstant()),
+                            savingsAccountTransactionData.getAmount(), balanceEndDate,
+                            savingsAccountTransactionData.getBalanceNumberOfDays(), savingsAccountTransactionData.getRunningBalance(),
+                            savingsAccountTransactionData.getCumulativeBalance(), savingsAccountTransactionData.getSubmittedOnDate(),
+                            Integer.valueOf(1), savingsAccountTransactionData.isManualTransaction(),
+                            savingsAccountTransactionData.getRefNo() });
+                }
+            }
+            savingsAccountData.setUpdatedTransactions(savingsAccountTransactionDataList);
+        }
+
+        if (transRefNo.size() > 0) {
+            this.jdbcTemplate.batchUpdate(queryForSavingsUpdate, paramsForSavingsSummary);
+            this.jdbcTemplate.batchUpdate(queryForTransactionInsertion, paramsForTransactionInsertion);
+
+            List<SavingsAccountTransactionData> savingsAccountTransactionDataList = fetchTransactionsFromIds(transRefNo);
+            if (savingsAccountDataList != null) {
+                LOG.info("Fetched Transactions from DB: {}", savingsAccountTransactionDataList.size());
+            }
+
+            HashMap<String, SavingsAccountTransactionData> savingsAccountTransactionMap = new HashMap<>();
+            for (SavingsAccountTransactionData savingsAccountTransactionData : savingsAccountTransactionDataList) {
+                final String key = savingsAccountTransactionData.getRefNo();
+                savingsAccountTransactionMap.put(key, savingsAccountTransactionData);
+            }
+            batchUpdateJournalEntries(savingsAccountDataList, savingsAccountTransactionMap);
+        }
+
+    }
+
+    private String batchQueryForTransactionInsertion() {
+        StringBuilder query = new StringBuilder(100);
+        query.append("INSERT INTO m_savings_account_transaction (savings_account_id, office_id, is_reversed,");
+        query.append("transaction_type_enum, transaction_date, amount, balance_end_date_derived,");
+        query.append("balance_number_of_days_derived, running_balance_derived, cumulative_balance_derived,");
+        query.append("created_date, appuser_id, is_manual, is_loan_disbursement, ref_no) VALUES ");
+        query.append("(?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?)");
+        return query.toString();
+
+    }
+
+    private String batchQueryForSavingsSummaryUpdate() {
+        StringBuilder query = new StringBuilder(100);
+        query.append("update m_savings_account set total_deposits_derived=?, total_withdrawals_derived=?, ");
+        query.append("total_interest_earned_derived=?, total_interest_posted_derived=?, total_withdrawal_fees_derived=?, ");
+        query.append("total_fees_charge_derived=?, total_penalty_charge_derived=?, total_annual_fees_derived=?, ");
+        query.append("account_balance_derived=?, total_overdraft_interest_derived=?, total_withhold_tax_derived=?, ");
+        query.append("last_interest_calculation_date=?, interest_posted_till_date=? where id=?");
+        return query.toString();
+    }
 }
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 c684440..e8d1b30 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
@@ -21,28 +21,36 @@ package org.apache.fineract.portfolio.savings.service;
 import static org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType.ACTIVE;
 
 import java.time.LocalDate;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
 import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
 import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
 import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
 import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
-import org.springframework.util.CollectionUtils;
+import org.springframework.transaction.support.TransactionTemplate;
 
 @Service
 public class SavingsSchedularServiceImpl implements SavingsSchedularService {
@@ -54,17 +62,27 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
     private final SavingsAccountReadPlatformService savingAccountReadPlatformService;
     private final SavingsAccountRepositoryWrapper savingsAccountRepository;
     private final ApplicationContext applicationContext;
+    private final ConfigurationDomainService configurationDomainService;
+    private final JdbcTemplate jdbcTemplate;
+    private final TransactionTemplate transactionTemplate;
+    private Queue<List<SavingsAccountData>> queue = new ArrayDeque<>();
+    private int queueSize = 1;
 
     @Autowired
     public SavingsSchedularServiceImpl(final SavingsAccountAssembler savingAccountAssembler,
             final SavingsAccountWritePlatformService savingsAccountWritePlatformService,
             final SavingsAccountReadPlatformService savingAccountReadPlatformService,
-            final SavingsAccountRepositoryWrapper savingsAccountRepository, final ApplicationContext applicationContext) {
+            final SavingsAccountRepositoryWrapper savingsAccountRepository, final ApplicationContext applicationContext,
+            final ConfigurationDomainService configurationDomainService, final RoutingDataSource dataSource,
+            final TransactionTemplate transactionTemplate) {
         this.savingAccountAssembler = savingAccountAssembler;
         this.savingsAccountWritePlatformService = savingsAccountWritePlatformService;
         this.savingAccountReadPlatformService = savingAccountReadPlatformService;
         this.savingsAccountRepository = savingsAccountRepository;
         this.applicationContext = applicationContext;
+        this.configurationDomainService = configurationDomainService;
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+        this.transactionTemplate = transactionTemplate;
     }
 
     @Override
@@ -77,28 +95,43 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
         Long maxSavingsIdInList = 0L;
         // initialise the executor service with fetched configurations
         final ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
+        final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig();
 
-        List<Long> savingsIds = Collections.synchronizedList(
-                this.savingAccountReadPlatformService.getAccountsIdsByStatusPaged(ACTIVE.getValue(), pageSize, maxSavingsIdInList));
-        if (!CollectionUtils.isEmpty(savingsIds)) {
-            do {
-                int totalFilteredRecords = savingsIds.size();
-                LOG.info("Starting Interest posting - total filtered records - {}", totalFilteredRecords);
-                postInterest(savingsIds, threadPoolSize, batchSize, executorService);
-                maxSavingsIdInList = savingsIds.get(savingsIds.size() - 1);
-                savingsIds = Collections.synchronizedList(
-                        this.savingAccountReadPlatformService.getAccountsIdsByStatusPaged(ACTIVE.getValue(), pageSize, maxSavingsIdInList));
-            } while (!CollectionUtils.isEmpty(savingsIds));
+        long start = System.currentTimeMillis();
+
+        LOG.info("Reading Savings Account Data!");
+        List<SavingsAccountData> savingsAccounts = this.savingAccountReadPlatformService
+                .retrieveAllSavingsDataForInterestPosting(backdatedTxnsAllowedTill, pageSize, ACTIVE.getValue(), maxSavingsIdInList);
+
+        if (savingsAccounts != null && savingsAccounts.size() > 0) {
+            savingsAccounts = Collections.synchronizedList(savingsAccounts);
+            long finish = System.currentTimeMillis();
+            LOG.info("Done fetching Data within {} milliseconds", finish - start);
+            if (savingsAccounts != null) {
+                queue.add(savingsAccounts);
+            }
+
+            if (!CollectionUtils.isEmpty(queue)) {
+                do {
+                    int totalFilteredRecords = savingsAccounts.size();
+                    LOG.info("Starting Interest posting - total records - {}", totalFilteredRecords);
+                    List<SavingsAccountData> queueElement = queue.element();
+                    maxSavingsIdInList = queueElement.get(queueElement.size() - 1).getId();
+                    postInterest(queue.remove(), threadPoolSize, batchSize, executorService, backdatedTxnsAllowedTill, pageSize,
+                            maxSavingsIdInList);
+                } while (!CollectionUtils.isEmpty(queue));
+            }
+            // shutdown the executor when done
+            executorService.shutdownNow();
         }
-        // shutdown the executor when done
-        executorService.shutdownNow();
     }
 
-    private void postInterest(List<Long> savingsIds, int threadPoolSize, int batchSize, ExecutorService executorService) {
+    private void postInterest(List<SavingsAccountData> savingsAccounts, int threadPoolSize, int batchSize, ExecutorService executorService,
+            final boolean backdatedTxnsAllowedTill, final int pageSize, Long maxSavingsIdInList) {
         List<Callable<Void>> posters = new ArrayList<>();
         int fromIndex = 0;
         // get the size of current paginated dataset
-        int size = savingsIds.size();
+        int size = savingsAccounts.size();
         // calculate the batch size
         batchSize = (int) Math.ceil((double) size / threadPoolSize);
 
@@ -107,21 +140,50 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
         }
 
         int toIndex = (batchSize > size - 1) ? size : batchSize;
-        while (toIndex < size && savingsIds.get(toIndex - 1).equals(savingsIds.get(toIndex))) {
+        while (toIndex < size && savingsAccounts.get(toIndex - 1).getId().equals(savingsAccounts.get(toIndex).getId())) {
             toIndex++;
         }
         boolean lastBatch = false;
         int loopCount = size / batchSize + 1;
 
+        FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
+        Long finalMaxSavingsIdInList = maxSavingsIdInList;
+
+        Callable<Void> fetchData = () -> {
+            ThreadLocalContextUtil.setTenant(tenant);
+            Long maxId = finalMaxSavingsIdInList;
+            if (!queue.isEmpty()) {
+                maxId = Math.max(finalMaxSavingsIdInList, queue.element().get(queue.element().size() - 1).getId());
+            }
+
+            while (queue.size() <= queueSize) {
+                LOG.info("Fetching while threads are running!");
+                List<SavingsAccountData> savingsAccountDataList = Collections.synchronizedList(this.savingAccountReadPlatformService
+                        .retrieveAllSavingsDataForInterestPosting(backdatedTxnsAllowedTill, pageSize, ACTIVE.getValue(), maxId));
+                if (savingsAccountDataList == null || savingsAccountDataList.isEmpty()) {
+                    break;
+                }
+                maxId = savingsAccountDataList.get(savingsAccountDataList.size() - 1).getId();
+                queue.add(savingsAccountDataList);
+            }
+            return null;
+        };
+        posters.add(fetchData);
+
         for (long i = 0; i < loopCount; i++) {
-            List<Long> subList = safeSubList(savingsIds, fromIndex, toIndex);
+            List<SavingsAccountData> subList = safeSubList(savingsAccounts, fromIndex, toIndex);
             SavingsSchedularInterestPoster poster = (SavingsSchedularInterestPoster) this.applicationContext
                     .getBean("savingsSchedularInterestPoster");
-            poster.setSavingsIds(subList);
-            poster.setTenant(ThreadLocalContextUtil.getTenant());
+            poster.setSavings(subList);
+            poster.setTenant(tenant);
             poster.setSavingsAccountWritePlatformService(savingsAccountWritePlatformService);
+            poster.setSavingsAccountReadPlatformService(savingAccountReadPlatformService);
             poster.setSavingsAccountRepository(savingsAccountRepository);
             poster.setSavingAccountAssembler(savingAccountAssembler);
+            poster.setJdbcTemplate(jdbcTemplate);
+            poster.setBackdatedTxnsAllowedTill(backdatedTxnsAllowedTill);
+            poster.setTransactionTemplate(transactionTemplate);
+
             posters.add(poster);
 
             if (lastBatch) {
@@ -132,13 +194,32 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService {
             }
             fromIndex = fromIndex + (toIndex - fromIndex);
             toIndex = (toIndex + batchSize > size - 1) ? size : toIndex + batchSize;
-            while (toIndex < size && savingsIds.get(toIndex - 1).equals(savingsIds.get(toIndex))) {
+            while (toIndex < size && savingsAccounts.get(toIndex - 1).getId().equals(savingsAccounts.get(toIndex).getId())) {
                 toIndex++;
             }
         }
+
         try {
             List<Future<Void>> responses = executorService.invokeAll(posters);
+            Long maxId = maxSavingsIdInList;
+            if (!queue.isEmpty()) {
+                maxId = Math.max(maxSavingsIdInList, queue.element().get(queue.element().size() - 1).getId());
+            }
+
+            while (queue.size() <= queueSize) {
+                LOG.info("Fetching while threads are running!..:: this is not supposed to run........");
+                savingsAccounts = Collections.synchronizedList(this.savingAccountReadPlatformService
+                        .retrieveAllSavingsDataForInterestPosting(backdatedTxnsAllowedTill, pageSize, ACTIVE.getValue(), maxId));
+                if (savingsAccounts == null || savingsAccounts.isEmpty()) {
+                    break;
+                }
+                maxId = savingsAccounts.get(savingsAccounts.size() - 1).getId();
+                LOG.info("Add to the Queue");
+                queue.add(savingsAccounts);
+            }
+
             checkCompletion(responses);
+            LOG.info("Queue size {}", queue.size());
         } catch (InterruptedException e1) {
             LOG.error("Interrupted while postInterest", e1);
         }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentData.java
index f0cc2e7..93c38ca 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentData.java
@@ -109,4 +109,60 @@ public final class TaxComponentData implements Serializable {
         this.glAccountTypeOptions = glAccountTypeOptions;
     }
 
+    private TaxComponentData(final Long id, final BigDecimal percentage, final GLAccountData debitAcount,
+            final GLAccountData creditAcount) {
+        this.id = id;
+        this.percentage = percentage;
+        this.name = null;
+        this.debitAccountType = null;
+        this.debitAccount = debitAcount;
+        this.creditAccountType = null;
+        this.creditAccount = creditAcount;
+        this.startDate = null;
+        this.taxComponentHistories = null;
+        this.glAccountOptions = null;
+        this.glAccountTypeOptions = null;
+    }
+
+    public static TaxComponentData createTaxComponent(final Long id, final BigDecimal percentage, final GLAccountData debitAccount,
+            final GLAccountData creditAccount) {
+        return new TaxComponentData(id, percentage, debitAccount, creditAccount);
+    }
+
+    public BigDecimal getApplicablePercentage(final LocalDate date) {
+        BigDecimal percentage = null;
+        if (occursOnDayFrom(date)) {
+            percentage = getPercentage();
+        } else {
+            for (TaxComponentHistoryData componentHistory : this.taxComponentHistories) {
+                if (componentHistory.occursOnDayFromAndUpToAndIncluding(date)) {
+                    percentage = componentHistory.getPercentage();
+                    break;
+                }
+            }
+        }
+        return percentage;
+    }
+
+    public BigDecimal getPercentage() {
+        return this.percentage;
+    }
+
+    private boolean occursOnDayFrom(final LocalDate target) {
+        return target != null && target.isAfter(startDate());
+    }
+
+    public LocalDate startDate() {
+        LocalDate startDate = null;
+        if (this.startDate != null) {
+            startDate = this.startDate;
+            // startDate = LocalDate.ofInstant(this.startDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
+        }
+        return startDate;
+    }
+
+    public GLAccountData getCreditAcount() {
+        return this.creditAccount;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java
index 87000cd..086f65d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxComponentHistoryData.java
@@ -36,4 +36,31 @@ public class TaxComponentHistoryData implements Serializable {
         this.startDate = startDate;
         this.endDate = endDate;
     }
+
+    public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
+        if (this.endDate == null) {
+            return target != null && target.isAfter(startDate());
+        }
+        return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
+    }
+
+    public LocalDate startDate() {
+        LocalDate startDate = null;
+        if (this.startDate != null) {
+            startDate = this.startDate;
+        }
+        return startDate;
+    }
+
+    public LocalDate endDate() {
+        LocalDate endDate = null;
+        if (this.endDate != null) {
+            endDate = this.endDate;
+        }
+        return endDate;
+    }
+
+    public BigDecimal getPercentage() {
+        return this.percentage;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxDetailsData.java
similarity index 59%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxDetailsData.java
index b98b38f..d0cdb5d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxDetailsData.java
@@ -18,24 +18,32 @@
  */
 package org.apache.fineract.portfolio.tax.data;
 
-import java.io.Serializable;
-import java.time.LocalDate;
-
-public class TaxGroupMappingsData implements Serializable {
-
-    @SuppressWarnings("unused")
-    private final Long id;
-    @SuppressWarnings("unused")
-    private final TaxComponentData taxComponent;
-    @SuppressWarnings("unused")
-    private final LocalDate startDate;
-    @SuppressWarnings("unused")
-    private final LocalDate endDate;
-
-    public TaxGroupMappingsData(final Long id, final TaxComponentData taxComponent, final LocalDate startDate, final LocalDate endDate) {
-        this.id = id;
+import java.math.BigDecimal;
+import org.apache.fineract.organisation.monetary.domain.Money;
+
+public class TaxDetailsData {
+
+    private TaxComponentData taxComponent;
+
+    private BigDecimal amount;
+
+    protected TaxDetailsData() {}
+
+    public TaxDetailsData(final TaxComponentData taxComponent, final BigDecimal amount) {
         this.taxComponent = taxComponent;
-        this.startDate = startDate;
-        this.endDate = endDate;
+        this.amount = amount;
+    }
+
+    public TaxComponentData getTaxComponent() {
+        return this.taxComponent;
+    }
+
+    public BigDecimal getAmount() {
+        return this.amount;
     }
+
+    public void updateAmount(Money amount) {
+        this.amount = amount.getAmount();
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java
index d42f713..dfb5179 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupData.java
@@ -61,4 +61,8 @@ public final class TaxGroupData implements Serializable {
         this.taxComponents = taxComponents;
     }
 
+    public Collection<TaxGroupMappingsData> getTaxAssociations() {
+        return this.taxAssociations;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
index b98b38f..f0009cd 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/data/TaxGroupMappingsData.java
@@ -38,4 +38,31 @@ public class TaxGroupMappingsData implements Serializable {
         this.startDate = startDate;
         this.endDate = endDate;
     }
+
+    public TaxComponentData getTaxComponent() {
+        return this.taxComponent;
+    }
+
+    public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
+        if (this.endDate == null) {
+            return target != null && target.isAfter(startDate());
+        }
+        return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
+    }
+
+    public LocalDate startDate() {
+        LocalDate startDate = null;
+        if (this.startDate != null) {
+            startDate = this.startDate;
+        }
+        return startDate;
+    }
+
+    public LocalDate endDate() {
+        LocalDate endDate = null;
+        if (this.endDate != null) {
+            endDate = this.endDate;
+        }
+        return endDate;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxUtils.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxUtils.java
index fcc8242..ee5b0cd 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxUtils.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/tax/service/TaxUtils.java
@@ -25,6 +25,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
+import org.apache.fineract.portfolio.tax.data.TaxComponentData;
+import org.apache.fineract.portfolio.tax.data.TaxGroupMappingsData;
 import org.apache.fineract.portfolio.tax.domain.TaxComponent;
 import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
 
@@ -55,6 +57,27 @@ public final class TaxUtils {
         return map;
     }
 
+    public static Map<TaxComponentData, BigDecimal> splitTaxData(final BigDecimal amount, final LocalDate date,
+            final Set<TaxGroupMappingsData> taxGroupMappings, final int scale) {
+        Map<TaxComponentData, BigDecimal> map = new HashMap<>(3);
+        if (amount != null) {
+            final double amountVal = amount.doubleValue();
+            double cent_percentage = Double.parseDouble("100.0");
+            for (TaxGroupMappingsData groupMappings : taxGroupMappings) {
+                if (groupMappings.occursOnDayFromAndUpToAndIncluding(date)) {
+                    TaxComponentData component = groupMappings.getTaxComponent();
+                    BigDecimal percentage = component.getApplicablePercentage(date);
+                    if (percentage != null) {
+                        double percentageVal = percentage.doubleValue();
+                        double tax = amountVal * percentageVal / cent_percentage;
+                        map.put(component, BigDecimal.valueOf(tax).setScale(scale, MoneyHelper.getRoundingMode()));
+                    }
+                }
+            }
+        }
+        return map;
+    }
+
     public static BigDecimal incomeAmount(final BigDecimal amount, final LocalDate date, final Set<TaxGroupMappings> taxGroupMappings,
             final int scale) {
         Map<TaxComponent, BigDecimal> map = splitTax(amount, date, taxGroupMappings, scale);
@@ -74,6 +97,14 @@ public final class TaxUtils {
         return totalTax;
     }
 
+    public static BigDecimal totalTaxDataAmount(final Map<TaxComponentData, BigDecimal> map) {
+        BigDecimal totalTax = BigDecimal.ZERO;
+        for (BigDecimal tax : map.values()) {
+            totalTax = totalTax.add(tax);
+        }
+        return totalTax;
+    }
+
     public static BigDecimal addTax(final BigDecimal amount, final LocalDate date, final List<TaxGroupMappings> taxGroupMappings,
             final int scale) {
         BigDecimal totalAmount = null;
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V391__add_transaction_ref_column.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V391__add_transaction_ref_column.sql
new file mode 100644
index 0000000..d94b2d4
--- /dev/null
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V391__add_transaction_ref_column.sql
@@ -0,0 +1,22 @@
+--
+-- 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.
+--
+
+ALTER TABLE `m_savings_account_transaction` ADD COLUMN `ref_no` VARCHAR(128) DEFAULT NULL;
+
+ALTER TABLE `m_savings_account_transaction` ADD UNIQUE INDEX `transaction_ref_no` (`ref_no`);