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

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


The following commit(s) were added to refs/heads/develop by this push:
     new a6fa1adc99 FINERACT-2312: Post interest with adjustments for savings 
accounts
a6fa1adc99 is described below

commit a6fa1adc99f62af7882930e5b4dbaee01077d7ff
Author: Juan-Pablo-Alvarez <work_...@hotmail.com>
AuthorDate: Wed Jul 23 18:09:25 2025 -0600

    FINERACT-2312: Post interest with adjustments for savings accounts
---
 .../portfolio/savings/data/SavingsAccountData.java |  10 +
 .../data/SavingsAccountTransactionData.java        |  71 +++-
 .../domain/interest/CompoundInterestHelper.java    |   8 +-
 .../domain/interest/CompoundInterestValues.java    |   2 +-
 .../savings/domain/interest/PostingPeriod.java     |  10 +-
 .../SavingsAccountInterestPostingServiceImpl.java  | 143 ++++++-
 .../SavingsAccountReadPlatformServiceImpl.java     |  17 +
 ...countWritePlatformServiceJpaRepositoryImpl.java |  18 +
 .../service/SavingsSchedularInterestPoster.java    |  52 +--
 .../SavingsInterestPostingTest.java                | 443 +++++++++++++++++++++
 .../common/savings/SavingsProductHelper.java       |  13 +-
 11 files changed, 726 insertions(+), 61 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
index e1394661ce..42c5ab2fb3 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 import lombok.Getter;
+import lombok.Setter;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
@@ -47,6 +48,7 @@ import org.apache.fineract.portfolio.tax.data.TaxGroupData;
 /**
  * Immutable data object representing a savings account.
  */
+@Setter
 @Getter
 @JsonLocalDateArrayFormat
 public final class SavingsAccountData implements Serializable {
@@ -144,6 +146,13 @@ public final class SavingsAccountData implements 
Serializable {
     private transient Long glAccountIdForSavingsControl;
     private transient Long glAccountIdForInterestOnSavings;
 
+    private Long glAccountIdForInterestPayable;
+    private Long glAccountIdForOverdraftPorfolio;
+    private Long glAccountIdForInterestReceivable;
+
+    private BigDecimal interestPosting;
+    private BigDecimal overdraftPosting;
+
     public static SavingsAccountData importInstanceIndividual(Long clientId, 
Long productId, Long fieldOfficerId, LocalDate submittedOnDate,
             BigDecimal nominalAnnualInterestRate, EnumOptionData 
interestCompoundingPeriodTypeEnum,
             EnumOptionData interestPostingPeriodTypeEnum, EnumOptionData 
interestCalculationTypeEnum,
@@ -964,4 +973,5 @@ public final class SavingsAccountData implements 
Serializable {
     public boolean isIsDormancyTrackingActive() {
         return this.isDormancyTrackingActive;
     }
+
 }
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
index bf1028b5fd..47236df405 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
@@ -105,6 +105,10 @@ public final class SavingsAccountTransactionData 
implements Serializable {
     private BigDecimal overdraftAmount;
     private transient Long modifiedId;
     private transient String refNo;
+    private Boolean isOverdraft;
+
+    private Long accountCredit;
+    private Long accountDebit;
 
     private SavingsAccountTransactionData(final Long id, final 
SavingsAccountTransactionEnumData transactionType,
             final PaymentDetailData paymentDetailData, final Long savingsId, 
final String savingsAccountNo, final LocalDate transactionDate,
@@ -112,7 +116,7 @@ public final class SavingsAccountTransactionData implements 
Serializable {
             final boolean reversed, final AccountTransferData transfer, final 
Collection<PaymentTypeData> paymentTypeOptions,
             final LocalDate submittedOnDate, final boolean 
interestedPostedAsOn, final String submittedByUsername, final String note,
             final Boolean isReversal, final Long originalTransactionId, 
boolean isManualTransaction, final Boolean lienTransaction,
-            final Long releaseTransactionId, final String reasonForBlock) {
+            final Long releaseTransactionId, final String reasonForBlock, 
final Boolean isOverdraft) {
         this.id = id;
         this.transactionType = transactionType;
         TransactionEntryType entryType = null;
@@ -146,6 +150,7 @@ public final class SavingsAccountTransactionData implements 
Serializable {
         this.lienTransaction = lienTransaction;
         this.releaseTransactionId = releaseTransactionId;
         this.reasonForBlock = reasonForBlock;
+        this.isOverdraft = isOverdraft;
     }
 
     private static SavingsAccountTransactionData createData(final Long id, 
final SavingsAccountTransactionEnumData transactionType,
@@ -156,7 +161,7 @@ public final class SavingsAccountTransactionData implements 
Serializable {
             final Boolean lienTransaction) {
         return new SavingsAccountTransactionData(id, transactionType, 
paymentDetailData, accountId, accountNo, date, currency, amount,
                 outstandingChargeAmount, runningBalance, reversed, transfer, 
paymentTypeOptions, submittedOnDate, interestedPostedAsOn,
-                submittedByUsername, note, null, null, false, lienTransaction, 
null, null);
+                submittedByUsername, note, null, null, false, lienTransaction, 
null, null, false);
     }
 
     public static SavingsAccountTransactionData create(final Long id, final 
SavingsAccountTransactionEnumData transactionType,
@@ -167,7 +172,8 @@ public final class SavingsAccountTransactionData implements 
Serializable {
             final Boolean lienTransaction, final Long releaseTransactionId, 
final String reasonForBlock) {
         return new SavingsAccountTransactionData(id, transactionType, 
paymentDetailData, savingsId, savingsAccountNo, date, currency,
                 amount, outstandingChargeAmount, runningBalance, reversed, 
transfer, null, submittedOnDate, interestedPostedAsOn,
-                submittedByUsername, note, isReversal, originalTransactionId, 
false, lienTransaction, releaseTransactionId, reasonForBlock);
+                submittedByUsername, note, isReversal, originalTransactionId, 
false, lienTransaction, releaseTransactionId, reasonForBlock,
+                false);
     }
 
     public static SavingsAccountTransactionData create(final Long id, final 
SavingsAccountTransactionEnumData transactionType,
@@ -235,10 +241,10 @@ public final class SavingsAccountTransactionData 
implements Serializable {
     private static SavingsAccountTransactionData createImport(final 
SavingsAccountTransactionEnumData transactionType,
             final PaymentDetailData paymentDetailData, final Long 
savingsAccountId, final String accountNumber,
             final LocalDate transactionDate, final BigDecimal 
transactionAmount, final boolean reversed, final LocalDate submittedOnDate,
-            boolean isManualTransaction, final Boolean lienTransaction) {
+            boolean isManualTransaction, final Boolean lienTransaction, final 
Boolean isOverdraft) {
         SavingsAccountTransactionData data = new 
SavingsAccountTransactionData(null, transactionType, paymentDetailData, 
savingsAccountId,
                 accountNumber, transactionDate, null, transactionAmount, null, 
null, reversed, null, null, submittedOnDate, false, null,
-                null, null, null, isManualTransaction, lienTransaction, null, 
null);
+                null, null, null, isManualTransaction, lienTransaction, null, 
null, isOverdraft);
         // duplicated import fields
         data.savingsAccountId = savingsAccountId;
         data.accountNumber = accountNumber;
@@ -251,14 +257,14 @@ public final class SavingsAccountTransactionData 
implements Serializable {
         return createImport(accountTransaction.getTransactionType(), 
accountTransaction.getPaymentDetailData(),
                 accountTransaction.getSavingsAccountId(), null, 
accountTransaction.getTransactionDate(), accountTransaction.getAmount(),
                 accountTransaction.isReversed(), 
accountTransaction.getSubmittedOnDate(), 
accountTransaction.isManualTransaction(),
-                accountTransaction.getLienTransaction());
+                accountTransaction.getLienTransaction(), false);
     }
 
     public static SavingsAccountTransactionData importInstance(BigDecimal 
transactionAmount, LocalDate transactionDate, Long paymentTypeId,
             String accountNumber, String checkNumber, String routingCode, 
String receiptNumber, String bankNumber, String note,
             Long savingsAccountId, SavingsAccountTransactionEnumData 
transactionType, Integer rowIndex, String locale, String dateFormat) {
         SavingsAccountTransactionData data = createImport(transactionType, 
null, savingsAccountId, accountNumber, transactionDate,
-                transactionAmount, false, transactionDate, false, false);
+                transactionAmount, false, transactionDate, false, false, 
false);
         data.rowIndex = rowIndex;
         data.paymentTypeId = paymentTypeId;
         data.checkNumber = checkNumber;
@@ -272,10 +278,11 @@ public final class SavingsAccountTransactionData 
implements Serializable {
     }
 
     private static SavingsAccountTransactionData 
createImport(SavingsAccountTransactionEnumData transactionType, Long 
savingsAccountId,
-            LocalDate transactionDate, BigDecimal transactionAmount, final 
LocalDate submittedOnDate, boolean isManualTransaction) {
+            LocalDate transactionDate, BigDecimal transactionAmount, final 
LocalDate submittedOnDate, boolean isManualTransaction,
+            Boolean isOverdraft) {
         // import transaction
         return createImport(transactionType, null, savingsAccountId, null, 
transactionDate, transactionAmount, false, submittedOnDate,
-                isManualTransaction, false);
+                isManualTransaction, false, isOverdraft);
     }
 
     public static SavingsAccountTransactionData interestPosting(final 
SavingsAccountData savingsAccount, final LocalDate date,
@@ -285,17 +292,28 @@ public final class SavingsAccountTransactionData 
implements Serializable {
         SavingsAccountTransactionEnumData transactionType = new 
SavingsAccountTransactionEnumData(
                 savingsAccountTransactionType.getValue().longValue(), 
savingsAccountTransactionType.getCode(),
                 savingsAccountTransactionType.getValue().toString());
-        return createImport(transactionType, savingsAccount.getId(), date, 
amount.getAmount(), submittedOnDate, isManualTransaction);
+        return createImport(transactionType, savingsAccount.getId(), date, 
amount.getAmount(), submittedOnDate, isManualTransaction, false);
+    }
+
+    public static SavingsAccountTransactionData accrual(final 
SavingsAccountData savingsAccount, final LocalDate date, final Money amount,
+            final boolean isManualTransaction) {
+        final LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
+        final SavingsAccountTransactionType savingsAccountTransactionType = 
SavingsAccountTransactionType.ACCRUAL;
+        SavingsAccountTransactionEnumData transactionType = new 
SavingsAccountTransactionEnumData(
+                savingsAccountTransactionType.getValue().longValue(), 
savingsAccountTransactionType.getCode(),
+                savingsAccountTransactionType.getValue().toString());
+        return createImport(transactionType, savingsAccount.getId(), date, 
amount.getAmount(), submittedOnDate, isManualTransaction, false);
     }
 
     public static SavingsAccountTransactionData overdraftInterest(final 
SavingsAccountData savingsAccount, final LocalDate date,
-            final Money amount, final boolean isManualTransaction) {
+            final Money amount, final boolean isManualTransaction, final 
Boolean isOverdraft) {
         final LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
         final SavingsAccountTransactionType savingsAccountTransactionType = 
SavingsAccountTransactionType.OVERDRAFT_INTEREST;
         SavingsAccountTransactionEnumData transactionType = new 
SavingsAccountTransactionEnumData(
                 savingsAccountTransactionType.getValue().longValue(), 
savingsAccountTransactionType.getCode(),
                 savingsAccountTransactionType.getValue().toString());
-        return createImport(transactionType, savingsAccount.getId(), date, 
amount.getAmount(), submittedOnDate, isManualTransaction);
+        return createImport(transactionType, savingsAccount.getId(), date, 
amount.getAmount(), submittedOnDate, isManualTransaction,
+                isOverdraft);
     }
 
     public static SavingsAccountTransactionData withHoldTax(final 
SavingsAccountData savingsAccount, final LocalDate date,
@@ -306,7 +324,7 @@ public final class SavingsAccountTransactionData implements 
Serializable {
                 savingsAccountTransactionType.getValue().longValue(), 
savingsAccountTransactionType.getCode(),
                 savingsAccountTransactionType.getValue().toString());
         SavingsAccountTransactionData accountTransaction = 
createImport(transactionType, savingsAccount.getId(), date, amount.getAmount(),
-                submittedOnDate, false);
+                submittedOnDate, false, false);
         accountTransaction.addTaxDetails(taxDetails);
         return accountTransaction;
     }
@@ -387,10 +405,11 @@ public final class SavingsAccountTransactionData 
implements Serializable {
         final MonetaryCurrency currency = openingBalance.getCurrency();
         Money endOfDayBalance = openingBalance.copy();
         if (isDeposit() || isDividendPayoutAndNotReversed()) {
-            endOfDayBalance = openingBalance.plus(getAmount());
+            endOfDayBalance = Money.of(currency, this.runningBalance);
         } else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
-
-            if (openingBalance.isGreaterThanZero()) {
+            if (isWithdrawal()) {
+                endOfDayBalance = Money.of(currency, this.runningBalance);
+            } else if (openingBalance.isGreaterThanZero()) {
                 endOfDayBalance = openingBalance.minus(getAmount());
             } else {
                 endOfDayBalance = Money.of(currency, this.runningBalance);
@@ -400,6 +419,14 @@ public final class SavingsAccountTransactionData 
implements Serializable {
         return EndOfDayBalance.from(getTransactionDate(), openingBalance, 
endOfDayBalance, this.balanceNumberOfDays);
     }
 
+    public EndOfDayBalance toEndOfDayBalanceDates(final Money openingBalance, 
LocalDateInterval date) {
+        final MonetaryCurrency currency = openingBalance.getCurrency();
+        Money endOfDayBalance = Money.of(currency, this.runningBalance);
+
+        return EndOfDayBalance.from(getTransactionDate(), openingBalance, 
endOfDayBalance,
+                this.balanceNumberOfDays != null ? this.balanceNumberOfDays : 
date.endDate().getDayOfMonth());
+    }
+
     public boolean isChargeTransactionAndNotReversed() {
         return this.transactionType.isChargeTransaction() && isNotReversed();
     }
@@ -427,7 +454,9 @@ public final class SavingsAccountTransactionData implements 
Serializable {
             if (isDeposit() || isDividendPayoutAndNotReversed()) {
                 endOfDayBalance = endOfDayBalance.plus(getAmount());
             } else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
-                if (endOfDayBalance.isGreaterThanZero() || isAllowOverdraft) {
+                if (endOfDayBalance.isLessThanZero() && isAllowOverdraft) {
+                    endOfDayBalance = Money.of(currency, this.runningBalance);
+                } else if (endOfDayBalance.isGreaterThanZero() || 
isAllowOverdraft) {
                     endOfDayBalance = endOfDayBalance.minus(getAmount());
                 } else {
                     endOfDayBalance = Money.of(currency, this.runningBalance);
@@ -658,4 +687,12 @@ public final class SavingsAccountTransactionData 
implements Serializable {
     public TransactionEntryType getEntryType() {
         return entryType;
     }
+
+    public void setAccountCredit(Long accountCredit) {
+        this.accountCredit = accountCredit;
+    }
+
+    public void setAccountDebit(Long accountDebit) {
+        this.accountDebit = accountDebit;
+    }
 }
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestHelper.java
 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestHelper.java
index 4b918c0198..46f588dd66 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestHelper.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestHelper.java
@@ -47,9 +47,14 @@ public class CompoundInterestHelper {
         // total interest earned in previous periods but not yet recognised
         BigDecimal compoundedInterest = BigDecimal.ZERO;
         BigDecimal unCompoundedInterest = BigDecimal.ZERO;
+        LocalDate endDay = DateUtils.getBusinessLocalDate();
         final CompoundInterestValues compoundInterestValues = new 
CompoundInterestValues(compoundedInterest, unCompoundedInterest);
         for (final PostingPeriod postingPeriod : allPeriods) {
 
+            if (postingPeriod.dateOfPostingTransaction().getMonth() != 
endDay.getMonth()) {
+                
compoundInterestValues.setCompoundedInterest(interestEarned.getAmount());
+            }
+
             final BigDecimal interestEarnedThisPeriod = 
postingPeriod.calculateInterest(compoundInterestValues);
 
             final Money moneyToBePostedForPeriod = Money.of(currency, 
interestEarnedThisPeriod);
@@ -61,8 +66,9 @@ public class CompoundInterestHelper {
             // calculation.
             if (!(postingPeriod.isInterestTransfered() || 
!interestTransferEnabled
                     || (lockUntil != null && 
!DateUtils.isAfter(postingPeriod.dateOfPostingTransaction(), lockUntil)))) {
-                compoundInterestValues.setcompoundedInterest(BigDecimal.ZERO);
+                compoundInterestValues.setCompoundedInterest(BigDecimal.ZERO);
             }
+            endDay = postingPeriod.dateOfPostingTransaction();
         }
 
         return interestEarned;
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestValues.java
 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestValues.java
index 09a871e8db..68cfc07cd7 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestValues.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/CompoundInterestValues.java
@@ -38,7 +38,7 @@ public class CompoundInterestValues {
         return this.uncompoundedInterest;
     }
 
-    public void setcompoundedInterest(BigDecimal interestToBeCompounded) {
+    public void setCompoundedInterest(BigDecimal interestToBeCompounded) {
         this.compoundedInterest = interestToBeCompounded;
     }
 
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java
 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java
index a9decdc097..65130e998c 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/domain/interest/PostingPeriod.java
@@ -26,6 +26,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.TreeSet;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -34,6 +36,8 @@ import 
org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodTyp
 import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
 import 
org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
 
+@Setter
+@Getter
 public final class PostingPeriod {
 
     private final LocalDateInterval periodInterval;
@@ -64,6 +68,8 @@ public final class PostingPeriod {
 
     private Integer financialYearBeginningMonth;
 
+    private boolean overdraftInterest = false;
+
     public void setOverdraftInterestRateAsFraction(BigDecimal 
overdraftInterestRateAsFraction) {
         this.overdraftInterestRateAsFraction = overdraftInterestRateAsFraction;
     }
@@ -301,7 +307,7 @@ public final class PostingPeriod {
 
             if 
(compoundingPeriodEndDate.equals(compoundingPeriod.getPeriodInterval().endDate()))
 {
                 BigDecimal interestCompounded = 
compoundInterestValues.getcompoundedInterest().add(unCompoundedInterest);
-                
compoundInterestValues.setcompoundedInterest(interestCompounded);
+                
compoundInterestValues.setCompoundedInterest(interestCompounded);
                 compoundInterestValues.setZeroForInterestToBeUncompounded();
             }
             interestEarned = interestEarned.add(interestUnrounded);
@@ -314,7 +320,7 @@ public final class PostingPeriod {
     }
 
     public Money getInterestEarned() {
-        return this.interestEarnedRounded;
+        return this.interestEarnedRounded != null ? this.interestEarnedRounded 
: Money.zero(this.currency);
     }
 
     private static List<CompoundingPeriod> 
compoundingPeriodsInPostingPeriod(final LocalDateInterval postingPeriodInterval,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
index b952b24a51..79831d54f5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
@@ -32,6 +32,7 @@ import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.savings.DepositAccountType;
@@ -81,11 +82,17 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
         for (final PostingPeriod interestPostingPeriod : postingPeriods) {
             final LocalDate interestPostingTransactionDate = 
interestPostingPeriod.dateOfPostingTransaction();
             final Money interestEarnedToBePostedForPeriod = 
interestPostingPeriod.getInterestEarned();
+            final Boolean isOverdraft = 
interestPostingPeriod.isOverdraftInterest();
 
             if (!DateUtils.isAfter(interestPostingTransactionDate, 
interestPostingUpToDate)) {
                 interestPostedToDate = 
interestPostedToDate.plus(interestEarnedToBePostedForPeriod);
-                final SavingsAccountTransactionData postingTransaction = 
findInterestPostingTransactionFor(interestPostingTransactionDate,
-                        savingsAccountData);
+                SavingsAccountTransactionData postingTransaction = null;
+                if 
(this.depositAccountType(savingsAccountData).isSavingsDeposit() && 
savingsAccountData.isAllowOverdraft()) {
+                    postingTransaction = 
findInterestPostingTransactionForInterest(interestPostingTransactionDate, 
savingsAccountData,
+                            isOverdraft);
+                } else {
+                    postingTransaction = 
findInterestPostingTransactionFor(interestPostingTransactionDate, 
savingsAccountData);
+                }
 
                 if (postingTransaction == null) {
                     SavingsAccountTransactionData newPostingTransaction;
@@ -95,7 +102,7 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
                     } else {
                         newPostingTransaction = 
SavingsAccountTransactionData.overdraftInterest(savingsAccountData,
                                 interestPostingTransactionDate, 
interestEarnedToBePostedForPeriod.negated(),
-                                interestPostingPeriod.isUserPosting());
+                                interestPostingPeriod.isUserPosting(), 
isOverdraft);
                     }
 
                     
savingsAccountData.updateTransactions(newPostingTransaction);
@@ -131,7 +138,7 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
                         } else {
                             newPostingTransaction = 
SavingsAccountTransactionData.overdraftInterest(savingsAccountData,
                                     interestPostingTransactionDate, 
interestEarnedToBePostedForPeriod.negated(),
-                                    interestPostingPeriod.isUserPosting());
+                                    interestPostingPeriod.isUserPosting(), 
isOverdraft);
                         }
 
                         
savingsAccountData.updateTransactions(newPostingTransaction);
@@ -190,6 +197,36 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
         return transaction;
     }
 
+    private Money appendPostingPeriodIfAny(final LocalDateInterval 
periodInterval, Money periodStartingBalance,
+            final List<SavingsAccountTransactionData> txs, final 
MonetaryCurrency monetaryCurrency,
+            final SavingsCompoundingInterestPeriodType compoundingPeriodType, 
final SavingsInterestCalculationType interestCalculationType,
+            final BigDecimal interestRateAsFraction, final int daysInYear, 
final LocalDate upToInterestCalculationDate,
+            final Collection<Long> interestPostTransactions, final boolean 
isInterestTransfer, final Money minBalanceForInterestCalculation,
+            final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final 
BigDecimal overdraftInterestRateAsFraction,
+            final Money minOverdraftForInterestCalculation, final boolean 
isUserPosting, final Integer financialYearBeginningMonth,
+            final boolean allowOverdraft, final List<PostingPeriod> 
allPostingPeriods, Boolean isOverdraftTransacction) {
+
+        if (txs == null || txs.isEmpty()) {
+            return periodStartingBalance;
+        }
+
+        final PostingPeriod postingPeriod = 
PostingPeriod.createFromDTO(periodInterval, periodStartingBalance, txs, 
monetaryCurrency,
+                compoundingPeriodType, interestCalculationType, 
interestRateAsFraction, daysInYear, upToInterestCalculationDate,
+                interestPostTransactions, isInterestTransfer, 
minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
+                overdraftInterestRateAsFraction, 
minOverdraftForInterestCalculation, isUserPosting, financialYearBeginningMonth,
+                allowOverdraft);
+
+        periodStartingBalance = postingPeriod.closingBalance();
+        postingPeriod.setOverdraftInterest(isOverdraftTransacction);
+
+        if (!(MathUtil.isZero(postingPeriod.getOpeningBalance().getAmount())
+                && 
MathUtil.isZero(postingPeriod.closingBalance().getAmount()))) {
+            allPostingPeriods.add(postingPeriod);
+        }
+
+        return periodStartingBalance;
+    }
+
     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) {
@@ -268,16 +305,40 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
             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, 
savingsAccountData.isAllowOverdraft());
+            if (savingsAccountData.isAllowOverdraft() && 
!MathUtil.isZero(savingsAccountData.getGlAccountIdForInterestReceivable())) {
 
-            periodStartingBalance = postingPeriod.closingBalance();
+                List<SavingsAccountTransactionData> overdraftTxs = 
listForOverdraft(savingsAccountData, periodInterval);
+                List<SavingsAccountTransactionData> interestPostingTxs = 
listForInterestPosting(savingsAccountData, periodInterval,
+                        monetaryCurrency);
 
-            allPostingPeriods.add(postingPeriod);
+                boolean isOverdraftAccountType = 
isOverdraftAccount(savingsAccountData, periodInterval, monetaryCurrency);
+
+                List<SavingsAccountTransactionData> primaryInterestPublication 
= isOverdraftAccountType ? overdraftTxs : interestPostingTxs;
+                List<SavingsAccountTransactionData> 
secondaryInterestPublication = isOverdraftAccountType ? interestPostingTxs
+                        : overdraftTxs;
+
+                periodStartingBalance = 
appendPostingPeriodIfAny(periodInterval, periodStartingBalance, 
primaryInterestPublication,
+                        monetaryCurrency, compoundingPeriodType, 
interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
+                        upToInterestCalculationDate, interestPostTransactions, 
isInterestTransfer, minBalanceForInterestCalculation,
+                        isSavingsInterestPostingAtCurrentPeriodEnd, 
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
+                        isUserPosting, financialYearBeginningMonth, 
savingsAccountData.isAllowOverdraft(), allPostingPeriods,
+                        isOverdraftAccountType ? true : false);
+
+                periodStartingBalance = 
appendPostingPeriodIfAny(periodInterval, periodStartingBalance, 
secondaryInterestPublication,
+                        monetaryCurrency, compoundingPeriodType, 
interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
+                        upToInterestCalculationDate, interestPostTransactions, 
isInterestTransfer, minBalanceForInterestCalculation,
+                        isSavingsInterestPostingAtCurrentPeriodEnd, 
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
+                        isUserPosting, financialYearBeginningMonth, 
savingsAccountData.isAllowOverdraft(), allPostingPeriods,
+                        isOverdraftAccountType ? false : true);
+
+            } else {
+                periodStartingBalance = 
appendPostingPeriodIfAny(periodInterval, periodStartingBalance,
+                        
retreiveOrderedNonInterestPostingTransactions(savingsAccountData), 
monetaryCurrency, compoundingPeriodType,
+                        interestCalculationType, interestRateAsFraction, 
daysInYearType.getValue(), upToInterestCalculationDate,
+                        interestPostTransactions, isInterestTransfer, 
minBalanceForInterestCalculation,
+                        isSavingsInterestPostingAtCurrentPeriodEnd, 
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
+                        isUserPosting, financialYearBeginningMonth, 
savingsAccountData.isAllowOverdraft(), allPostingPeriods, false);
+            }
         }
 
         
this.savingsHelper.calculateInterestForAllPostingPeriods(monetaryCurrency, 
allPostingPeriods,
@@ -297,6 +358,52 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
         return allPostingPeriods;
     }
 
+    private List<SavingsAccountTransactionData> listForOverdraft(final 
SavingsAccountData savingsAccountData,
+            final LocalDateInterval periodInterval) {
+        List<SavingsAccountTransactionData> overdraftTransactionsInPeriod = 
new ArrayList<>();
+        for (SavingsAccountTransactionData lists : 
retreiveOrderedNonInterestPostingTransactions(savingsAccountData)) {
+            if (MathUtil.isLessThanZero(lists.getRunningBalance()) && 
periodInterval.startDate().getMonth() == lists.getDate().getMonth()) {
+                overdraftTransactionsInPeriod.add(lists);
+
+            }
+        }
+        return overdraftTransactionsInPeriod;
+
+    }
+
+    private List<SavingsAccountTransactionData> listForInterestPosting(final 
SavingsAccountData savingsAccountData,
+            final LocalDateInterval periodInterval, final MonetaryCurrency 
currency) {
+
+        final List<SavingsAccountTransactionData> nonOverdraftTransactions = 
new ArrayList<>();
+
+        for (final SavingsAccountTransactionData tx : 
retreiveOrderedNonInterestPostingTransactions(savingsAccountData)) {
+            if (periodInterval.startDate().getMonth() == 
tx.getDate().getMonth()) {
+                final Money runningBalance = Money.of(currency, 
tx.getRunningBalance());
+
+                if (runningBalance.isGreaterThanZero() && 
!runningBalance.isZero()) {
+                    nonOverdraftTransactions.add(tx);
+                }
+            }
+        }
+        return nonOverdraftTransactions;
+    }
+
+    private Boolean isOverdraftAccount(final SavingsAccountData 
savingsAccountData, final LocalDateInterval periodInterval,
+            final MonetaryCurrency currency) {
+
+        for (SavingsAccountTransactionData tx : 
retreiveOrderedNonInterestPostingTransactions(savingsAccountData)) {
+            if (MathUtil.isLessThanZero(tx.getRunningBalance()) && 
periodInterval.startDate().getMonth() == tx.getDate().getMonth()) {
+                return true;
+            } else if (periodInterval.startDate().getMonth() == 
tx.getDate().getMonth()) {
+                final Money runningBalance = Money.of(currency, 
tx.getRunningBalance());
+                if (!runningBalance.isZero()) {
+                    return false;
+                }
+            }
+        }
+        return false;
+    }
+
     private List<SavingsAccountTransactionData> 
retreiveOrderedNonInterestPostingTransactions(final SavingsAccountData 
savingsAccountData) {
         final List<SavingsAccountTransactionData> listOfTransactionsSorted = 
retrieveListOfTransactions(savingsAccountData);
 
@@ -509,6 +616,18 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
         return postingTransation;
     }
 
+    protected SavingsAccountTransactionData 
findInterestPostingTransactionForInterest(final LocalDate postingDate,
+            final SavingsAccountData savingsAccountData, boolean isOverdraft) {
+        SavingsAccountTransactionData postingTransation = null;
+        List<SavingsAccountTransactionData> transactions = 
savingsAccountData.getSavingsAccountTransactionData();
+        postingTransation = transactions.stream().filter(t -> {
+            Boolean interestSearch = isOverdraft ? 
t.isOverdraftInterestAndNotReversed() : t.isInterestPostingAndNotReversed();
+            return interestSearch && t.occursOn(postingDate) && 
!t.isReversalTransaction();
+        }).findFirst().orElse(null);
+
+        return postingTransation;
+    }
+
     protected void resetAccountTransactionsEndOfDayBalances(final 
List<SavingsAccountTransactionData> accountTransactionsSorted,
             final LocalDate interestPostingUpToDate, final SavingsAccountData 
savingsAccountData) {
         // loop over transactions in reverse
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 a5de90375b..96084d007b 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
@@ -337,6 +337,9 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
                     "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(
+                    "apm2.gl_account_id as 
glAccountIdForInterestReceivable,apm3.gl_account_id as 
glAccountIdForOverdraftPorfolio, ");
+            sqlBuilder.append("apm4.gl_account_id as 
glAccountIdForInterestPayable, ");
             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 ");
@@ -356,6 +359,9 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
                     "left join acc_product_mapping apm on apm.product_type = 2 
and apm.product_id = sp.id and apm.financial_account_type=3 ");
             sqlBuilder.append(
                     "left join acc_product_mapping apm1 on apm1.product_type = 
2 and apm1.product_id = sp.id and apm1.financial_account_type=2 ");
+            sqlBuilder.append("left join acc_product_mapping apm2 on 
apm2.product_id = sp.id and apm2.financial_account_type=18 ");
+            sqlBuilder.append("left join acc_product_mapping apm3 on 
apm3.product_id = sp.id and apm3.financial_account_type = 11 ");
+            sqlBuilder.append("left join acc_product_mapping apm4 on 
apm4.product_id = sp.id and apm4.financial_account_type = 17 ");
 
             this.schemaSql = sqlBuilder.toString();
         }
@@ -409,6 +415,11 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
                     final Long glAccountIdForInterestOnSavings = 
rs.getLong("glAccountIdForInterestOnSavings");
                     final Long glAccountIdForSavingsControl = 
rs.getLong("glAccountIdForSavingsControl");
 
+                    final Long glAccountIdForOverdraftPorfolio = 
rs.getLong("glAccountIdForOverdraftPorfolio");
+                    final Long glAccountIdForInterestReceivable = 
rs.getLong("glAccountIdForInterestReceivable");
+
+                    final Long glAccountIdForInterestPayable = 
rs.getLong("glAccountIdForInterestPayable");
+
                     final Long productId = rs.getLong("productId");
                     final Integer accountType = rs.getInt("accountingType");
                     final AccountingRuleType accountingRuleType = 
AccountingRuleType.fromInt(accountType);
@@ -565,6 +576,12 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
                     savingsAccountData.setClientData(clientData);
                     savingsAccountData.setGroupGeneralData(groupGeneralData);
                     savingsAccountData.setSavingsProduct(savingsProductData);
+
+                    
savingsAccountData.setGlAccountIdForInterestReceivable(glAccountIdForInterestReceivable);
+                    
savingsAccountData.setGlAccountIdForOverdraftPorfolio(glAccountIdForOverdraftPorfolio);
+
+                    
savingsAccountData.setGlAccountIdForInterestPayable(glAccountIdForInterestPayable);
+
                     
savingsAccountData.setGlAccountIdForInterestOnSavings(glAccountIdForInterestOnSavings);
                     
savingsAccountData.setGlAccountIdForSavingsControl(glAccountIdForSavingsControl);
                 }
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 b6705759cb..f30712b509 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
@@ -595,6 +595,7 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
                 for (SavingsAccountTransactionData accountTransaction : 
transactions) {
                     if (accountTransaction.getId() == null) {
                         
savingsAccountData.setNewSavingsAccountTransactionData(accountTransaction);
+                        selectAccountId(accountTransaction, 
savingsAccountData);
                     }
                 }
             }
@@ -604,6 +605,23 @@ public class 
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
         return savingsAccountData;
     }
 
+    public void selectAccountId(SavingsAccountTransactionData 
accountTransaction, SavingsAccountData savingsAccountData) {
+        SavingsAccountTransactionType transactionType = 
SavingsAccountTransactionType
+                
.fromInt(accountTransaction.getTransactionType().getId().intValue());
+        if (transactionType.isOverDraftInterestPosting()) {
+            if 
(MathUtil.isGreaterThanZero(accountTransaction.getRunningBalance())) {
+                
accountTransaction.setAccountDebit(savingsAccountData.getGlAccountIdForSavingsControl());
+                
accountTransaction.setAccountCredit(savingsAccountData.getGlAccountIdForInterestReceivable());
+            } else {
+                
accountTransaction.setAccountDebit(savingsAccountData.getGlAccountIdForOverdraftPorfolio());
+                
accountTransaction.setAccountCredit(savingsAccountData.getGlAccountIdForInterestReceivable());
+            }
+        } else {
+            
accountTransaction.setAccountDebit(savingsAccountData.getGlAccountIdForInterestPayable());
+            
accountTransaction.setAccountCredit(savingsAccountData.getGlAccountIdForSavingsControl());
+        }
+    }
+
     @Override
     public CommandProcessingResult reverseTransaction(final Long savingsId, 
final Long transactionId,
             final boolean allowAccountTransferModification, final JsonCommand 
command) {
diff --git 
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
 
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
index 295d3f55ae..913fbdb31e 100644
--- 
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
+++ 
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
@@ -108,33 +108,33 @@ public class SavingsSchedularInterestPoster {
             for (SavingsAccountTransactionData savingsAccountTransactionData : 
savingsAccountTransactionDataList) {
                 if (savingsAccountTransactionData.getId() == null) {
                     final String key = 
savingsAccountTransactionData.getRefNo();
-                    if (savingsAccountTransactionDataHashMap.containsKey(key)) 
{
-                        final SavingsAccountTransactionData dataFromFetch = 
savingsAccountTransactionDataHashMap.get(key);
-                        
savingsAccountTransactionData.setId(dataFromFetch.getId());
-                        if 
(savingsAccountData.getGlAccountIdForSavingsControl() != 0
-                                && 
savingsAccountData.getGlAccountIdForInterestOnSavings() != 0) {
-                            OffsetDateTime auditDatetime = 
DateUtils.getAuditOffsetDateTime();
-                            paramsForGLInsertion.add(new Object[] { 
savingsAccountData.getGlAccountIdForSavingsControl(),
-                                    savingsAccountData.getOfficeId(), null, 
currencyCode,
-                                    SAVINGS_TRANSACTION_IDENTIFIER + 
savingsAccountTransactionData.getId().toString(),
-                                    savingsAccountTransactionData.getId(), 
null, false, null, false,
-                                    
savingsAccountTransactionData.getTransactionDate(), 
JournalEntryType.CREDIT.getValue().longValue(),
-                                    savingsAccountTransactionData.getAmount(), 
null, JournalEntryType.CREDIT.getValue().longValue(),
-                                    savingsAccountData.getId(), auditDatetime, 
auditDatetime, false, BigDecimal.ZERO, BigDecimal.ZERO, null,
-                                    
savingsAccountTransactionData.getTransactionDate(), null, userId, userId,
-                                    DateUtils.getBusinessLocalDate() });
-
-                            paramsForGLInsertion.add(new Object[] { 
savingsAccountData.getGlAccountIdForInterestOnSavings(),
-                                    savingsAccountData.getOfficeId(), null, 
currencyCode,
-                                    SAVINGS_TRANSACTION_IDENTIFIER + 
savingsAccountTransactionData.getId().toString(),
-                                    savingsAccountTransactionData.getId(), 
null, false, null, false,
-                                    
savingsAccountTransactionData.getTransactionDate(), 
JournalEntryType.DEBIT.getValue().longValue(),
-                                    savingsAccountTransactionData.getAmount(), 
null, JournalEntryType.DEBIT.getValue().longValue(),
-                                    savingsAccountData.getId(), auditDatetime, 
auditDatetime, false, BigDecimal.ZERO, BigDecimal.ZERO, null,
-                                    
savingsAccountTransactionData.getTransactionDate(), null, userId, userId,
-                                    DateUtils.getBusinessLocalDate() });
-                        }
+                    final Boolean isOverdraft = 
savingsAccountTransactionData.getIsOverdraft();
+                    final SavingsAccountTransactionData dataFromFetch = 
savingsAccountTransactionDataHashMap.get(key);
+                    savingsAccountTransactionData.setId(dataFromFetch.getId());
+                    if (savingsAccountData.getGlAccountIdForSavingsControl() 
!= 0
+                            && 
savingsAccountData.getGlAccountIdForInterestOnSavings() != 0) {
+                        OffsetDateTime auditDatetime = 
DateUtils.getAuditOffsetDateTime();
+                        paramsForGLInsertion.add(
+                                new Object[] { 
savingsAccountTransactionData.getAccountCredit(), 
savingsAccountData.getOfficeId(), null,
+                                        currencyCode, 
SAVINGS_TRANSACTION_IDENTIFIER + 
savingsAccountTransactionData.getId().toString(),
+                                        savingsAccountTransactionData.getId(), 
null, false, null, false,
+                                        
savingsAccountTransactionData.getTransactionDate(), 
JournalEntryType.CREDIT.getValue().longValue(),
+                                        
savingsAccountTransactionData.getAmount(), null, 
JournalEntryType.CREDIT.getValue().longValue(),
+                                        savingsAccountData.getId(), 
auditDatetime, auditDatetime, false, BigDecimal.ZERO, BigDecimal.ZERO,
+                                        null, 
savingsAccountTransactionData.getTransactionDate(), null, userId, userId,
+                                        DateUtils.getBusinessLocalDate() });
+
+                        paramsForGLInsertion
+                                .add(new Object[] { 
savingsAccountTransactionData.getAccountDebit(), 
savingsAccountData.getOfficeId(), null,
+                                        currencyCode, 
SAVINGS_TRANSACTION_IDENTIFIER + 
savingsAccountTransactionData.getId().toString(),
+                                        savingsAccountTransactionData.getId(), 
null, false, null, false,
+                                        
savingsAccountTransactionData.getTransactionDate(), 
JournalEntryType.DEBIT.getValue().longValue(),
+                                        
savingsAccountTransactionData.getAmount(), null, 
JournalEntryType.DEBIT.getValue().longValue(),
+                                        savingsAccountData.getId(), 
auditDatetime, auditDatetime, false, BigDecimal.ZERO, BigDecimal.ZERO,
+                                        null, 
savingsAccountTransactionData.getTransactionDate(), null, userId, userId,
+                                        DateUtils.getBusinessLocalDate() });
                     }
+
                 }
             }
         }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingTest.java
new file mode 100644
index 0000000000..7e03e49487
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingTest.java
@@ -0,0 +1,443 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import 
org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.Account;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import 
org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
+import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SavingsInterestPostingTest {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SavingsInterestPostingTest.class);
+    private static ResponseSpecification responseSpec;
+    private static RequestSpecification requestSpec;
+    private AccountHelper accountHelper;
+    private SavingsAccountHelper savingsAccountHelper;
+    private SchedulerJobHelper schedulerJobHelper;
+    public static final String MINIMUM_OPENING_BALANCE = "1000.0";
+    private GlobalConfigurationHelper globalConfigurationHelper;
+    private SavingsProductHelper productHelper;
+    private JournalEntryHelper journalEntryHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec);
+        this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
+        this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, 
this.responseSpec);
+        this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, 
this.responseSpec);
+        this.globalConfigurationHelper = new GlobalConfigurationHelper();
+    }
+
+    @Test
+    public void testPostInterestWithOverdraftProduct() {
+        try {
+            final String amount = "10000";
+            final String jobName = "Post Interest For Savings";
+
+            final Account assetAccount = accountHelper.createAssetAccount();
+            final Account incomeAccount = accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
accountHelper.createExpenseAccount();
+            final Account liabilityAccount = 
accountHelper.createLiabilityAccount();
+            final Account interestReceivableAccount = 
accountHelper.createAssetAccount("interestReceivableAccount");
+            final Account savingsControlAccount = 
accountHelper.createLiabilityAccount("Savings Control");
+            final Account interestPayableAccount = 
accountHelper.createLiabilityAccount("Interest Payable");
+
+            final Integer productId = 
createSavingsProductWithAccrualAccountingWithOutOverdraftAllowed(
+                    interestPayableAccount.getAccountID().toString(), 
savingsControlAccount.getAccountID().toString(),
+                    interestReceivableAccount.getAccountID().toString(), 
assetAccount, incomeAccount, expenseAccount, liabilityAccount);
+
+            final Integer clientId = ClientHelper.createClient(requestSpec, 
responseSpec, "01 January 2025");
+            final LocalDate startDate = 
LocalDate.of(LocalDate.now(Utils.getZoneIdOfTenant()).getYear(), 2, 1);
+            final String startDateString = DateTimeFormatter.ofPattern("dd 
MMMM yyyy", Locale.US).format(startDate);
+
+            final Integer accountId = 
savingsAccountHelper.applyForSavingsApplicationOnDate(clientId, productId,
+                    SavingsAccountHelper.ACCOUNT_TYPE_INDIVIDUAL, 
startDateString);
+            savingsAccountHelper.approveSavingsOnDate(accountId, 
startDateString);
+            savingsAccountHelper.activateSavings(accountId, startDateString);
+            savingsAccountHelper.depositToSavingsAccount(accountId, amount, 
startDateString, CommonConstants.RESPONSE_RESOURCE_ID);
+
+            // Simulate time passing - update business date to March
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(true));
+            LocalDate marchDate = LocalDate.of(startDate.getYear(), 3, 1);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, marchDate);
+
+            schedulerJobHelper.executeAndAwaitJob(jobName);
+
+            long days = ChronoUnit.DAYS.between(startDate, marchDate);
+            BigDecimal expected = calcInterestPosting(productHelper, amount, 
days);
+
+            List<HashMap> txs = getInterestTransactions(accountId);
+            for (HashMap tx : txs) {
+                Assertions.assertEquals(expected, BigDecimal.valueOf(((Double) 
tx.get("amount"))));
+            }
+
+            long interestCount = countInterestOnDate(accountId, marchDate);
+            long overdraftCount = countOverdraftOnDate(accountId, marchDate);
+            Assertions.assertEquals(1L, interestCount, "Expected exactly one 
INTEREST posting on posting date");
+            Assertions.assertEquals(0L, overdraftCount, "Expected NO OVERDRAFT 
posting on posting date");
+        } finally {
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(false));
+        }
+    }
+
+    @Test
+    public void testOverdraftInterestWithOverdraftProduct() {
+        try {
+            final String amount = "10000";
+            final String jobName = "Post Interest For Savings";
+
+            final Account assetAccount = accountHelper.createAssetAccount();
+            final Account incomeAccount = accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
accountHelper.createExpenseAccount();
+            final Account liabilityAccount = 
accountHelper.createLiabilityAccount();
+            final Account interestReceivableAccount = 
accountHelper.createAssetAccount("interestReceivableAccount");
+            final Account savingsControlAccount = 
accountHelper.createLiabilityAccount("Savings Control");
+            final Account interestPayableAccount = 
accountHelper.createLiabilityAccount("Interest Payable");
+
+            final Integer productId = 
createSavingsProductWithAccrualAccountingWithOutOverdraftAllowed(
+                    interestPayableAccount.getAccountID().toString(), 
savingsControlAccount.getAccountID().toString(),
+                    interestReceivableAccount.getAccountID().toString(), 
assetAccount, incomeAccount, expenseAccount, liabilityAccount);
+
+            final Integer clientId = ClientHelper.createClient(requestSpec, 
responseSpec, "01 January 2025");
+            final LocalDate startDate = 
LocalDate.of(LocalDate.now(Utils.getZoneIdOfTenant()).getYear(), 2, 1);
+            final String startDateString = DateTimeFormatter.ofPattern("dd 
MMMM yyyy", Locale.US).format(startDate);
+
+            final Integer accountId = 
savingsAccountHelper.applyForSavingsApplicationOnDate(clientId, productId,
+                    SavingsAccountHelper.ACCOUNT_TYPE_INDIVIDUAL, 
startDateString);
+            savingsAccountHelper.approveSavingsOnDate(accountId, 
startDateString);
+            savingsAccountHelper.activateSavings(accountId, startDateString);
+            savingsAccountHelper.withdrawalFromSavingsAccount(accountId, 
amount, startDateString, CommonConstants.RESPONSE_RESOURCE_ID);
+
+            // Simulate time passing - update business date to March
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(true));
+            LocalDate marchDate = LocalDate.of(startDate.getYear(), 3, 1);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, marchDate);
+
+            schedulerJobHelper.executeAndAwaitJob(jobName);
+
+            long days = ChronoUnit.DAYS.between(startDate, marchDate);
+            BigDecimal expected = calcOverdraftPosting(productHelper, amount, 
days);
+
+            List<HashMap> txs = getInterestTransactions(accountId);
+            Assertions.assertEquals(expected, BigDecimal.valueOf(((Double) 
txs.get(0).get("amount"))));
+
+            BigDecimal runningBalance = BigDecimal.valueOf(((Double) 
txs.get(0).get("runningBalance")));
+            Assertions.assertTrue(MathUtil.isLessThanZero(runningBalance), 
"Running balance is not less than zero");
+
+            long interestCount = countInterestOnDate(accountId, marchDate);
+            long overdraftCount = countOverdraftOnDate(accountId, marchDate);
+            Assertions.assertEquals(0L, interestCount, "Expected NO INTEREST 
posting on posting date");
+            Assertions.assertEquals(1L, overdraftCount, "Expected exactly one 
OVERDRAFT posting on posting date");
+        } finally {
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(false));
+        }
+    }
+
+    @Test
+    public void 
testOverdraftAndInterestPosting_WithOverdraftProduct_WhitBalanceLessZero() {
+        try {
+            final String amountDeposit = "10000";
+            final String amountWithdrawal = "20000";
+            final String jobName = "Post Interest For Savings";
+
+            final Account assetAccount = accountHelper.createAssetAccount();
+            final Account incomeAccount = accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
accountHelper.createExpenseAccount();
+            final Account liabilityAccount = 
accountHelper.createLiabilityAccount();
+            final Account interestReceivableAccount = 
accountHelper.createAssetAccount("interestReceivableAccount");
+            final Account savingsControlAccount = 
accountHelper.createLiabilityAccount("Savings Control");
+            final Account interestPayableAccount = 
accountHelper.createLiabilityAccount("Interest Payable");
+
+            final Integer productId = 
createSavingsProductWithAccrualAccountingWithOutOverdraftAllowed(
+                    interestPayableAccount.getAccountID().toString(), 
savingsControlAccount.getAccountID().toString(),
+                    interestReceivableAccount.getAccountID().toString(), 
assetAccount, incomeAccount, expenseAccount, liabilityAccount);
+
+            final Integer clientId = ClientHelper.createClient(requestSpec, 
responseSpec, "01 January 2025");
+            final LocalDate startDate = 
LocalDate.of(LocalDate.now(Utils.getZoneIdOfTenant()).getYear(), 2, 1);
+            final String startStr = DateTimeFormatter.ofPattern("dd MMMM 
yyyy", Locale.US).format(startDate);
+
+            final Integer accountId = 
savingsAccountHelper.applyForSavingsApplicationOnDate(clientId, productId,
+                    SavingsAccountHelper.ACCOUNT_TYPE_INDIVIDUAL, startStr);
+            savingsAccountHelper.approveSavingsOnDate(accountId, startStr);
+            savingsAccountHelper.activateSavings(accountId, startStr);
+            savingsAccountHelper.depositToSavingsAccount(accountId, 
amountDeposit, startStr, CommonConstants.RESPONSE_RESOURCE_ID);
+
+            final LocalDate withdrawalDate = LocalDate.of(startDate.getYear(), 
2, 16);
+            final String withdrawalStr = DateTimeFormatter.ofPattern("dd MMMM 
yyyy", Locale.US).format(withdrawalDate);
+            savingsAccountHelper.withdrawalFromSavingsAccount(accountId, 
amountWithdrawal, withdrawalStr,
+                    CommonConstants.RESPONSE_RESOURCE_ID);
+
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(true));
+            LocalDate marchDate = LocalDate.of(startDate.getYear(), 3, 1);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, marchDate);
+
+            schedulerJobHelper.executeAndAwaitJob(jobName);
+
+            List<HashMap> txs = getInterestTransactions(accountId);
+            for (HashMap tx : txs) {
+                BigDecimal amt = BigDecimal.valueOf(((Double) 
tx.get("amount")));
+                @SuppressWarnings("unchecked")
+                Map<String, Object> typeMap = (Map<String, Object>) 
tx.get("transactionType");
+                SavingsAccountTransactionType type = 
SavingsAccountTransactionType.fromInt(((Double) typeMap.get("id")).intValue());
+
+                if (type.isInterestPosting()) {
+                    long days = ChronoUnit.DAYS.between(startDate, 
withdrawalDate);
+                    BigDecimal expected = calcInterestPosting(productHelper, 
amountDeposit, days);
+                    Assertions.assertEquals(expected, amt);
+                } else {
+                    long days = ChronoUnit.DAYS.between(withdrawalDate, 
marchDate);
+                    BigDecimal overdraftBase = new 
BigDecimal(amountWithdrawal).subtract(new BigDecimal(amountDeposit));
+                    BigDecimal expected = calcOverdraftPosting(productHelper, 
overdraftBase.toString(), days);
+                    Assertions.assertEquals(expected, amt);
+                }
+            }
+
+            Assertions.assertEquals(1L, countInterestOnDate(accountId, 
marchDate), "Expected exactly one INTEREST posting on posting date");
+            Assertions.assertEquals(1L, countOverdraftOnDate(accountId, 
marchDate),
+                    "Expected exactly one OVERDRAFT posting on posting date");
+        } finally {
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(false));
+        }
+    }
+
+    @Test
+    public void 
testOverdraftAndInterestPosting_WithOverdraftProduct_WhitBalanceGreaterZero() {
+        try {
+            final String amountDeposit = "20000";
+            final String amountWithdrawal = "10000";
+            final String jobName = "Post Interest For Savings";
+
+            final Account assetAccount = accountHelper.createAssetAccount();
+            final Account incomeAccount = accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
accountHelper.createExpenseAccount();
+            final Account liabilityAccount = 
accountHelper.createLiabilityAccount();
+            final Account interestReceivableAccount = 
accountHelper.createAssetAccount("interestReceivableAccount");
+            final Account savingsControlAccount = 
accountHelper.createLiabilityAccount("Savings Control");
+            final Account interestPayableAccount = 
accountHelper.createLiabilityAccount("Interest Payable");
+
+            final Integer productId = 
createSavingsProductWithAccrualAccountingWithOutOverdraftAllowed(
+                    interestPayableAccount.getAccountID().toString(), 
savingsControlAccount.getAccountID().toString(),
+                    interestReceivableAccount.getAccountID().toString(), 
assetAccount, incomeAccount, expenseAccount, liabilityAccount);
+
+            final Integer clientId = ClientHelper.createClient(requestSpec, 
responseSpec, "01 January 2025");
+            final LocalDate startDate = 
LocalDate.of(LocalDate.now(Utils.getZoneIdOfTenant()).getYear(), 2, 1);
+            final String startStr = DateTimeFormatter.ofPattern("dd MMMM 
yyyy", Locale.US).format(startDate);
+
+            final Integer accountId = 
savingsAccountHelper.applyForSavingsApplicationOnDate(clientId, productId,
+                    SavingsAccountHelper.ACCOUNT_TYPE_INDIVIDUAL, startStr);
+            savingsAccountHelper.approveSavingsOnDate(accountId, startStr);
+            savingsAccountHelper.activateSavings(accountId, startStr);
+            savingsAccountHelper.withdrawalFromSavingsAccount(accountId, 
amountWithdrawal, startStr, CommonConstants.RESPONSE_RESOURCE_ID);
+
+            final LocalDate depositDate = LocalDate.of(startDate.getYear(), 2, 
16);
+            final String depositStr = DateTimeFormatter.ofPattern("dd MMMM 
yyyy", Locale.US).format(depositDate);
+            savingsAccountHelper.depositToSavingsAccount(accountId, 
amountDeposit, depositStr, CommonConstants.RESPONSE_RESOURCE_ID);
+
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(true));
+            LocalDate marchDate = LocalDate.of(startDate.getYear(), 3, 1);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, marchDate);
+
+            schedulerJobHelper.executeAndAwaitJob(jobName);
+
+            List<HashMap> txs = getInterestTransactions(accountId);
+            for (HashMap tx : txs) {
+                BigDecimal amt = BigDecimal.valueOf(((Double) 
tx.get("amount")));
+                @SuppressWarnings("unchecked")
+                Map<String, Object> typeMap = (Map<String, Object>) 
tx.get("transactionType");
+                SavingsAccountTransactionType type = 
SavingsAccountTransactionType.fromInt(((Double) typeMap.get("id")).intValue());
+
+                if (type.isOverDraftInterestPosting()) {
+                    long days = ChronoUnit.DAYS.between(startDate, 
depositDate);
+                    BigDecimal expected = calcOverdraftPosting(productHelper, 
amountWithdrawal, days);
+                    Assertions.assertEquals(expected, amt);
+                } else {
+                    long days = ChronoUnit.DAYS.between(depositDate, 
marchDate);
+                    BigDecimal positiveBase = new 
BigDecimal(amountDeposit).subtract(new BigDecimal(amountWithdrawal));
+                    BigDecimal expected = calcInterestPosting(productHelper, 
positiveBase.toString(), days);
+                    Assertions.assertEquals(expected, amt);
+                }
+            }
+
+            Assertions.assertEquals(1L, countOverdraftOnDate(accountId, 
marchDate),
+                    "Expected exactly one OVERDRAFT posting on posting date");
+            Assertions.assertEquals(1L, countInterestOnDate(accountId, 
marchDate), "Expected exactly one INTEREST posting on posting date");
+        } finally {
+            
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(false));
+        }
+    }
+
+    private List<HashMap> getInterestTransactions(Integer savingsAccountId) {
+        List<HashMap> all = 
savingsAccountHelper.getSavingsTransactions(savingsAccountId);
+        List<HashMap> filtered = new ArrayList<>();
+        for (HashMap tx : all) {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> txType = (Map<String, Object>) 
tx.get("transactionType");
+            SavingsAccountTransactionType type = 
SavingsAccountTransactionType.fromInt(((Double) txType.get("id")).intValue());
+            if (type.isInterestPosting() || type.isOverDraftInterestPosting()) 
{
+                filtered.add(tx);
+            }
+        }
+        return filtered;
+    }
+
+    public Integer 
createSavingsProductWithAccrualAccountingWithOutOverdraftAllowed(final String 
interestPayableAccount,
+            final String savingsControlAccount, final String 
interestReceivableAccount, final Account... accounts) {
+
+        LOG.info("------------------------------CREATING NEW SAVINGS PRODUCT 
WITHOUT OVERDRAFT ---------------------------------------");
+        this.productHelper = new 
SavingsProductHelper().withOverDraftRate("100000", "21")
+                
.withAccountInterestReceivables(interestReceivableAccount).withSavingsControlAccountId(savingsControlAccount)
+                
.withInterestPayableAccountId(interestPayableAccount).withInterestCompoundingPeriodTypeAsAnnually()
+                
.withInterestPostingPeriodTypeAsMonthly().withInterestCalculationPeriodTypeAsDailyBalance()
+                .withAccountingRuleAsAccrualBased(accounts);
+
+        final String savingsProductJSON = this.productHelper.build();
+        return SavingsProductHelper.createSavingsProduct(savingsProductJSON, 
requestSpec, responseSpec);
+    }
+
+    private BigDecimal calcInterestPosting(SavingsProductHelper productHelper, 
String amount, long days) {
+        BigDecimal rate = 
productHelper.getNominalAnnualInterestRate().divide(new BigDecimal("100.00"));
+        BigDecimal principal = new BigDecimal(amount);
+        BigDecimal dayFactor = 
BigDecimal.ONE.divide(productHelper.getInterestCalculationDaysInYearType(), 
MathContext.DECIMAL64);
+        BigDecimal dailyRate = rate.multiply(dayFactor, MathContext.DECIMAL64);
+        BigDecimal periodRate = dailyRate.multiply(BigDecimal.valueOf(days), 
MathContext.DECIMAL64);
+        return principal.multiply(periodRate, 
MathContext.DECIMAL64).setScale(productHelper.getDecimalCurrency(), 
RoundingMode.HALF_EVEN);
+    }
+
+    private BigDecimal calcOverdraftPosting(SavingsProductHelper 
productHelper, String amount, long days) {
+        BigDecimal rate = 
productHelper.getNominalAnnualInterestRateOverdraft().divide(new 
BigDecimal("100.00"));
+        BigDecimal principal = new BigDecimal(amount);
+        BigDecimal dayFactor = 
BigDecimal.ONE.divide(productHelper.getInterestCalculationDaysInYearType(), 
MathContext.DECIMAL64);
+        BigDecimal dailyRate = rate.multiply(dayFactor, MathContext.DECIMAL64);
+        BigDecimal periodRate = dailyRate.multiply(BigDecimal.valueOf(days), 
MathContext.DECIMAL64);
+        return principal.multiply(periodRate, 
MathContext.DECIMAL64).setScale(productHelper.getDecimalCurrency(), 
RoundingMode.HALF_EVEN);
+    }
+
+    // =========================
+    // Helpers robustos de FECHA/TIPO
+    // =========================
+
+    @SuppressWarnings("unchecked")
+    private LocalDate coerceToLocalDate(HashMap tx) {
+        // Posibles claves de fecha devueltas por Fineract
+        String[] candidateKeys = new String[] { "date", "transactionDate", 
"submittedOnDate", "createdDate" };
+
+        for (String key : candidateKeys) {
+            Object v = tx.get(key);
+            if (v == null) {
+                continue;
+            }
+
+            // Caso 1: arreglo [yyyy, MM, dd]
+            if (v instanceof List<?>) {
+                List<?> arr = (List<?>) v;
+                if (arr.size() >= 3 && arr.get(0) instanceof Number && 
arr.get(1) instanceof Number && arr.get(2) instanceof Number) {
+                    int year = ((Number) arr.get(0)).intValue();
+                    int month = ((Number) arr.get(1)).intValue();
+                    int day = ((Number) arr.get(2)).intValue();
+                    return LocalDate.of(year, month, day);
+                }
+            }
+
+            // Caso 2: string con distintos formatos
+            if (v instanceof String) {
+                String s = (String) v;
+                DateTimeFormatter[] fmts = new DateTimeFormatter[] { 
DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.US),
+                        DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.US), 
DateTimeFormatter.ofPattern("yyyy-MM-dd") };
+                for (DateTimeFormatter f : fmts) {
+                    try {
+                        return LocalDate.parse(s, f);
+                    } catch (Exception ignore) {
+                        // intentionally ignored
+                    }
+                }
+            }
+        }
+        // Si ninguna clave/forma se pudo parsear
+        return null;
+    }
+
+    private boolean isDate(HashMap tx, LocalDate expected) {
+        LocalDate got = coerceToLocalDate(tx);
+        return got != null && got.isEqual(expected);
+    }
+
+    @SuppressWarnings("unchecked")
+    private SavingsAccountTransactionType txType(HashMap tx) {
+        Map<String, Object> typeMap = (Map<String, Object>) 
tx.get("transactionType");
+        return SavingsAccountTransactionType.fromInt(((Double) 
typeMap.get("id")).intValue());
+    }
+
+    private long countInterestOnDate(Integer accountId, LocalDate date) {
+        List<HashMap> all = 
savingsAccountHelper.getSavingsTransactions(accountId);
+        return all.stream().filter(tx -> isDate(tx, 
date)).map(this::txType).filter(SavingsAccountTransactionType::isInterestPosting)
+                .count();
+    }
+
+    private long countOverdraftOnDate(Integer accountId, LocalDate date) {
+        List<HashMap> all = 
savingsAccountHelper.getSavingsTransactions(accountId);
+        return all.stream().filter(tx -> isDate(tx, date)).map(this::txType)
+                
.filter(SavingsAccountTransactionType::isOverDraftInterestPosting).count();
+    }
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
index 2aa7725cc0..89a4f7487d 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
@@ -168,8 +168,8 @@ public class SavingsProductHelper {
             map.put("interestReceivableAccountId", 
this.interestReceivableAccountId);
         }
         if (this.accountingRule.equals(ACCRUAL_PERIODIC)) {
-            if (this.interestReceivableAccountId != null) {
-                map.put("interestReceivableAccountId", 
this.interestReceivableAccountId);
+            if (this.savingsControlAccountId != null) {
+                map.put("savingsControlAccountId", 
this.savingsControlAccountId);
             }
         }
 
@@ -202,6 +202,11 @@ public class SavingsProductHelper {
         return this;
     }
 
+    public SavingsProductHelper withInterestCompoundingPeriodTypeAsAnnually() {
+        this.interestCompoundingPeriodType = ANNUAL;
+        return this;
+    }
+
     public SavingsProductHelper withInterestPostingPeriodTypeAsMonthly() {
         this.interestPostingPeriodType = MONTHLY;
         return this;
@@ -357,6 +362,10 @@ public class SavingsProductHelper {
         return new BigDecimal(nominalAnnualInterestRate);
     }
 
+    public BigDecimal getNominalAnnualInterestRateOverdraft() {
+        return new BigDecimal(nominalAnnualInterestRateOverdraft);
+    }
+
     public BigDecimal getInterestCalculationDaysInYearType() {
         return new BigDecimal(interestCalculationDaysInYearType);
     }

Reply via email to