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 79f32d32a FINERACT-2081: Loan account data additional fields for 
summary and delinquency
79f32d32a is described below

commit 79f32d32a359d34ede8f00cb337d571da0dd984b
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Wed Jul 3 00:40:12 2024 -0600

    FINERACT-2081: Loan account data additional fields for summary and 
delinquency
---
 .../service/LoanDelinquencyDomainServiceImpl.java  |  39 +++++++
 .../portfolio/loanaccount/data/CollectionData.java |   7 +-
 .../loanaccount/data/LoanSummaryData.java          |  76 +++++++++++++-
 .../loanschedule/data/LoanSchedulePeriodData.java  |  16 ++-
 .../loan/LoanBusinessEventSerializer.java          |   3 +-
 .../loanaccount/api/LoansApiResource.java          |   4 +-
 .../loanaccount/api/LoansApiResourceSwagger.java   |  14 +++
 .../service/LoanReadPlatformServiceImpl.java       |  10 +-
 ...cyWritePlatformServiceRangeChangeEventTest.java |  42 ++++----
 ...PaymentAllocationLoanRepaymentScheduleTest.java |  80 +++++++++++++++
 .../DelinquencyBucketsIntegrationTest.java         | 114 +++++++++++++++++++++
 11 files changed, 371 insertions(+), 34 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
index d6ad0e5fc..c09a71479 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
@@ -64,6 +64,11 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
             return CollectionData.template();
         }
 
+        BigDecimal delinquentPrincipal = BigDecimal.ZERO;
+        BigDecimal delinquentInterest = BigDecimal.ZERO;
+        BigDecimal delinquentFee = BigDecimal.ZERO;
+        BigDecimal delinquentPenalty = BigDecimal.ZERO;
+
         // Get the oldest overdue installment if exists one
         for (LoanRepaymentScheduleInstallment installment : 
loan.getRepaymentScheduleInstallments()) {
             if (!installment.isObligationsMet()) {
@@ -71,6 +76,10 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
                     log.debug("Loan Id: {} with installment {} due date {}", 
loan.getId(), installment.getInstallmentNumber(),
                             installment.getDueDate());
                     outstandingAmount = 
outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount());
+                    delinquentPrincipal = 
delinquentPrincipal.add(installment.getPrincipalOutstanding(loanCurrency).getAmount());
+                    delinquentInterest = 
delinquentInterest.add(installment.getInterestOutstanding(loanCurrency).getAmount());
+                    delinquentFee = 
delinquentFee.add(installment.getFeeChargesOutstanding(loanCurrency).getAmount());
+                    delinquentPenalty = 
delinquentPenalty.add(installment.getPenaltyChargesOutstanding(loanCurrency).getAmount());
                     if (!oldestOverdueInstallment) {
                         log.debug("Oldest installment {} {}", 
installment.getInstallmentNumber(), installment.getDueDate());
                         CollectionData overDueInstallmentDelinquentData = 
calculateDelinquencyDataForOverdueInstallment(loan, installment);
@@ -85,6 +94,10 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
                     CollectionData nonOverDueInstallmentDelinquentData = 
calculateDelinquencyDataForNonOverdueInstallment(loan,
                             installment);
                     outstandingAmount = 
outstandingAmount.add(nonOverDueInstallmentDelinquentData.getDelinquentAmount());
+                    delinquentPrincipal = 
delinquentPrincipal.add(nonOverDueInstallmentDelinquentData.getDelinquentPrincipal());
+                    delinquentInterest = 
delinquentInterest.add(nonOverDueInstallmentDelinquentData.getDelinquentInterest());
+                    delinquentFee = 
delinquentFee.add(nonOverDueInstallmentDelinquentData.getDelinquentFee());
+                    delinquentPenalty = 
delinquentPenalty.add(nonOverDueInstallmentDelinquentData.getDelinquentPenalty());
                     if (!overdueSinceDateWasSet) {
                         overdueSinceDate = 
nonOverDueInstallmentDelinquentData.getDelinquentDate();
                         overdueSinceDateWasSet = true;
@@ -110,6 +123,11 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
             collectionData.setDelinquentDate(overdueSinceDate);
         }
         collectionData.setDelinquentAmount(outstandingAmount);
+        collectionData.setDelinquentPrincipal(delinquentPrincipal);
+        collectionData.setDelinquentInterest(delinquentInterest);
+        collectionData.setDelinquentFee(delinquentFee);
+        collectionData.setDelinquentPenalty(delinquentPenalty);
+
         collectionData.setDelinquentDays(0L);
         Long delinquentDays = overdueDays - graceDays;
         if (delinquentDays > 0) {
@@ -248,8 +266,17 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         LocalDate overdueSinceDate = null;
         CollectionData collectionData = CollectionData.template();
         BigDecimal outstandingAmount = BigDecimal.ZERO;
+        BigDecimal delinquentPrincipal = BigDecimal.ZERO;
+        BigDecimal delinquentInterest = BigDecimal.ZERO;
+        BigDecimal delinquentFee = BigDecimal.ZERO;
+        BigDecimal delinquentPenalty = BigDecimal.ZERO;
 
         outstandingAmount = 
outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount());
+        delinquentPrincipal = 
delinquentPrincipal.add(installment.getPrincipalOutstanding(loanCurrency).getAmount());
+        delinquentInterest = 
delinquentInterest.add(installment.getInterestOutstanding(loanCurrency).getAmount());
+        delinquentFee = 
delinquentFee.add(installment.getFeeChargesOutstanding(loanCurrency).getAmount());
+        delinquentPenalty = 
delinquentPenalty.add(installment.getPenaltyChargesOutstanding(loanCurrency).getAmount());
+
         overdueSinceDate = installment.getDueDate();
         BigDecimal amountAvailable = 
installment.getTotalPaid(loanCurrency).getAmount();
         boolean isLatestInstallment = Objects.equals(installment.getId(), 
latestInstallment.getId());
@@ -272,6 +299,10 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         }
         collectionData.setDelinquentDate(overdueSinceDate);
         collectionData.setDelinquentAmount(outstandingAmount);
+        collectionData.setDelinquentPrincipal(delinquentPrincipal);
+        collectionData.setDelinquentInterest(delinquentInterest);
+        collectionData.setDelinquentFee(delinquentFee);
+        collectionData.setDelinquentPenalty(delinquentPenalty);
         return collectionData;
     }
 
@@ -283,6 +314,10 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         LocalDate overdueSinceDate = null;
         CollectionData collectionData = CollectionData.template();
         BigDecimal outstandingAmount = BigDecimal.ZERO;
+        BigDecimal delinquentPrincipal = BigDecimal.ZERO;
+        BigDecimal delinquentInterest = BigDecimal.ZERO;
+        BigDecimal delinquentFee = BigDecimal.ZERO;
+        BigDecimal delinquentPenalty = BigDecimal.ZERO;
 
         List<LoanTransaction> chargebackTransactions = 
loan.getLoanTransactions(LoanTransaction::isChargeback);
         BigDecimal amountAvailable = 
installment.getTotalPaid(loanCurrency).getAmount();
@@ -306,6 +341,10 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         }
         collectionData.setDelinquentDate(overdueSinceDate);
         collectionData.setDelinquentAmount(outstandingAmount);
+        collectionData.setDelinquentPrincipal(delinquentPrincipal);
+        collectionData.setDelinquentInterest(delinquentInterest);
+        collectionData.setDelinquentFee(delinquentFee);
+        collectionData.setDelinquentPenalty(delinquentPenalty);
         return collectionData;
     }
 
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
index f872d6d27..fa4e4f6c8 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
@@ -47,9 +47,14 @@ public final class CollectionData {
     public Collection<DelinquencyPausePeriod> delinquencyPausePeriods;
     public Collection<InstallmentLevelDelinquency> installmentLevelDelinquency;
 
+    private BigDecimal delinquentPrincipal;
+    private BigDecimal delinquentInterest;
+    private BigDecimal delinquentFee;
+    private BigDecimal delinquentPenalty;
+
     public static CollectionData template() {
         final BigDecimal zero = BigDecimal.ZERO;
-        return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, 
null, zero, null, null);
+        return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, 
null, zero, null, null, zero, zero, zero, zero);
     }
 
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanSummaryData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanSummaryData.java
index 42ebbd6be..1d3cf5a63 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanSummaryData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanSummaryData.java
@@ -24,8 +24,12 @@ import java.util.Collection;
 import lombok.Builder;
 import lombok.Data;
 import lombok.experimental.Accessors;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
 import org.springframework.util.CollectionUtils;
 
 /**
@@ -91,11 +95,15 @@ public class LoanSummaryData {
     private BigDecimal totalCreditBalanceRefundReversed;
     private BigDecimal totalRepaymentTransaction;
     private BigDecimal totalRepaymentTransactionReversed;
+    private BigDecimal totalInterestPaymentWaiver;
     private final Long chargeOffReasonId;
     private final String chargeOffReason;
 
+    private BigDecimal totalUnpaidAccruedDueInterest;
+    private BigDecimal totalUnpaidAccruedNotDueInterest;
+
     public static LoanSummaryData withTransactionAmountsSummary(final 
LoanSummaryData defaultSummaryData,
-            final Collection<LoanTransactionData> loanTransactions) {
+            final Collection<LoanTransactionData> loanTransactions, final 
LoanScheduleData repaymentSchedule) {
 
         BigDecimal totalMerchantRefund = BigDecimal.ZERO;
         BigDecimal totalMerchantRefundReversed = BigDecimal.ZERO;
@@ -110,6 +118,9 @@ public class LoanSummaryData {
         BigDecimal totalCreditBalanceRefundReversed = BigDecimal.ZERO;
         BigDecimal totalRepaymentTransaction = BigDecimal.ZERO;
         BigDecimal totalRepaymentTransactionReversed = BigDecimal.ZERO;
+        BigDecimal totalInterestPaymentWaiver = BigDecimal.ZERO;
+        BigDecimal totalUnpaidAccruedDueInterest = BigDecimal.ZERO;
+        BigDecimal totalUnpaidAccruedNotDueInterest = BigDecimal.ZERO;
 
         if (!CollectionUtils.isEmpty(loanTransactions)) {
 
@@ -131,6 +142,24 @@ public class LoanSummaryData {
                     loanTransactions);
             totalRepaymentTransaction = 
computeTotalRepaymentTransactionAmount(loanTransactions);
             totalRepaymentTransactionReversed = 
computeTotalAmountForReversedTransactions(LoanTransactionType.REPAYMENT, 
loanTransactions);
+            totalInterestPaymentWaiver = 
computeTotalAmountForNonReversedTransactions(LoanTransactionType.INTEREST_PAYMENT_WAIVER,
+                    loanTransactions);
+        }
+
+        if (repaymentSchedule != null) {
+            // Accrued Due Interest on Past due installments
+            totalUnpaidAccruedDueInterest = 
computeTotalAccruedDueInterestAmount(repaymentSchedule.getPeriods());
+            if (MathUtil.isGreaterThanZero(totalUnpaidAccruedDueInterest)) {
+                totalUnpaidAccruedDueInterest = totalUnpaidAccruedDueInterest
+                        
.subtract(computeTotalInterestPaidDueAmount(repaymentSchedule.getPeriods()));
+            }
+
+            // Accrued Due Interest on Actual Installment
+            totalUnpaidAccruedNotDueInterest = 
computeTotalAccruedNotDueInterestAmountOnActualPeriod(repaymentSchedule.getPeriods());
+            if (MathUtil.isGreaterThanZero(totalUnpaidAccruedNotDueInterest)) {
+                totalUnpaidAccruedNotDueInterest = 
totalUnpaidAccruedNotDueInterest
+                        
.subtract(computeTotalInterestPaidNotDueAmountOnActualPeriod(repaymentSchedule.getPeriods()));
+            }
         }
 
         return 
LoanSummaryData.builder().currency(defaultSummaryData.currency).principalDisbursed(defaultSummaryData.principalDisbursed)
@@ -163,7 +192,9 @@ public class LoanSummaryData {
                 
.totalChargeAdjustment(totalChargeAdjustment).totalChargeAdjustmentReversed(totalChargeAdjustmentReversed)
                 
.totalChargeback(totalChargeback).totalCreditBalanceRefund(totalCreditBalanceRefund)
                 
.totalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed).totalRepaymentTransaction(totalRepaymentTransaction)
-                
.totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).build();
+                
.totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).totalInterestPaymentWaiver(totalInterestPaymentWaiver)
+                .totalUnpaidAccruedDueInterest(totalUnpaidAccruedDueInterest)
+                
.totalUnpaidAccruedNotDueInterest(totalUnpaidAccruedNotDueInterest).build();
     }
 
     public static LoanSummaryData withOnlyCurrencyData(CurrencyData 
currencyData) {
@@ -191,4 +222,45 @@ public class LoanSummaryData {
                 loanTransactions);
         return totalRepaymentTransaction.add(totalDownPaymentTransaction);
     }
+
+    private static BigDecimal 
computeTotalAccruedDueInterestAmount(Collection<LoanSchedulePeriodData> 
periods) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        return periods.stream().filter(period -> 
!period.getDownPaymentPeriod() && businessDate.isAfter(period.getDueDate()))
+                .map(period -> 
period.getTotalAccruedInterest()).reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private static BigDecimal 
computeTotalInterestPaidDueAmount(Collection<LoanSchedulePeriodData> periods) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        return periods.stream().filter(period -> 
!period.getDownPaymentPeriod() && businessDate.isAfter(period.getDueDate()))
+                .map(period -> 
period.getInterestPaid()).reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private static BigDecimal 
computeTotalAccruedNotDueInterestAmountOnActualPeriod(Collection<LoanSchedulePeriodData>
 periods) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        return periods.stream()
+                .filter(period -> !period.getDownPaymentPeriod() && 
isActualPeriod(period) && businessDate.isBefore(period.getDueDate()))
+                .map(period -> 
period.getTotalAccruedInterest()).reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private static BigDecimal 
computeTotalInterestPaidNotDueAmountOnActualPeriod(Collection<LoanSchedulePeriodData>
 periods) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        return periods.stream()
+                .filter(period -> !period.getDownPaymentPeriod() && 
isActualPeriod(period) && businessDate.isBefore(period.getDueDate()))
+                .map(period -> 
period.getInterestPaid()).reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private static boolean isActualPeriod(LoanSchedulePeriodData period) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        boolean actualPeriod = false;
+        if (period.getPeriod() != null) {
+            if (period.getPeriod() == 1) {
+                actualPeriod = ((businessDate.isEqual(period.getFromDate()) || 
businessDate.isAfter(period.getFromDate()))
+                        && businessDate.isBefore(period.getDueDate()));
+            } else {
+                actualPeriod = (businessDate.isAfter(period.getFromDate()) && 
businessDate.isBefore(period.getDueDate()));
+            }
+        }
+
+        return actualPeriod;
+    }
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
index fb50ee5f8..fcf050ee7 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
@@ -72,6 +72,7 @@ public final class LoanSchedulePeriodData {
     private final BigDecimal totalActualCostOfLoanForPeriod;
     private final BigDecimal totalInstallmentAmountForPeriod;
     private final BigDecimal totalCredits;
+    private final BigDecimal totalAccruedInterest;
     private final Boolean downPaymentPeriod;
 
     public static LoanSchedulePeriodData disbursementOnlyPeriod(final 
LocalDate disbursementDate, final BigDecimal principalDisbursed,
@@ -109,7 +110,8 @@ public final class LoanSchedulePeriodData {
             final BigDecimal totalDueForPeriod, final BigDecimal totalPaid, 
final BigDecimal totalPaidInAdvanceForPeriod,
             final BigDecimal totalPaidLateForPeriod, final BigDecimal 
totalWaived, final BigDecimal totalWrittenOff,
             final BigDecimal totalOutstanding, final BigDecimal 
totalActualCostOfLoanForPeriod,
-            final BigDecimal totalInstallmentAmountForPeriod, final BigDecimal 
totalCredits, final boolean isDownPayment) {
+            final BigDecimal totalInstallmentAmountForPeriod, final BigDecimal 
totalCredits, final boolean isDownPayment,
+            final BigDecimal totalAccruedInterest) {
 
         return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, 
obligationsMetOnDate, complete, principalOriginalDue,
                 principalPaid, principalWrittenOff, principalOutstanding, 
outstandingPrincipalBalanceOfLoan,
@@ -117,7 +119,7 @@ public final class LoanSchedulePeriodData {
                 feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, 
feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
                 penaltyChargesWaived, penaltyChargesWrittenOff, 
penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
                 totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, 
totalWaived, totalWrittenOff, totalOutstanding,
-                totalActualCostOfLoanForPeriod, 
totalInstallmentAmountForPeriod, totalCredits, isDownPayment);
+                totalActualCostOfLoanForPeriod, 
totalInstallmentAmountForPeriod, totalCredits, isDownPayment, 
totalAccruedInterest);
     }
 
     public static LoanSchedulePeriodData withPaidDetail(final 
LoanSchedulePeriodData loanSchedulePeriodData, final boolean complete,
@@ -138,7 +140,8 @@ public final class LoanSchedulePeriodData {
                 loanSchedulePeriodData.totalPaidLateForPeriod, 
loanSchedulePeriodData.totalWaivedForPeriod,
                 loanSchedulePeriodData.totalWrittenOffForPeriod, 
loanSchedulePeriodData.totalOutstandingForPeriod,
                 loanSchedulePeriodData.totalActualCostOfLoanForPeriod, 
loanSchedulePeriodData.totalInstallmentAmountForPeriod,
-                loanSchedulePeriodData.totalCredits, 
loanSchedulePeriodData.getDownPaymentPeriod());
+                loanSchedulePeriodData.totalCredits, 
loanSchedulePeriodData.getDownPaymentPeriod(),
+                loanSchedulePeriodData.totalAccruedInterest);
     }
 
     /*
@@ -202,6 +205,7 @@ public final class LoanSchedulePeriodData {
         this.totalInstallmentAmountForPeriod = null;
         this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? 
this.totalOutstandingForPeriod : null;
         this.totalCredits = BigDecimal.ZERO;
+        this.totalAccruedInterest = BigDecimal.ZERO;
         this.downPaymentPeriod = false;
     }
 
@@ -261,6 +265,7 @@ public final class LoanSchedulePeriodData {
         this.totalInstallmentAmountForPeriod = totalInstallmentAmountForPeriod;
         this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? 
this.totalOutstandingForPeriod : null;
         this.totalCredits = BigDecimal.ZERO;
+        this.totalAccruedInterest = BigDecimal.ZERO;
         this.downPaymentPeriod = false;
     }
 
@@ -316,6 +321,7 @@ public final class LoanSchedulePeriodData {
         this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? 
this.totalOutstandingForPeriod : null;
         this.totalCredits = BigDecimal.ZERO;
         this.downPaymentPeriod = true;
+        this.totalAccruedInterest = BigDecimal.ZERO;
     }
 
     /*
@@ -334,7 +340,7 @@ public final class LoanSchedulePeriodData {
             final BigDecimal totalPaid, final BigDecimal 
totalPaidInAdvanceForPeriod, final BigDecimal totalPaidLateForPeriod,
             final BigDecimal totalWaived, final BigDecimal totalWrittenOff, 
final BigDecimal totalOutstanding,
             final BigDecimal totalActualCostOfLoanForPeriod, final BigDecimal 
totalInstallmentAmountForPeriod,
-            final BigDecimal totalCredits, final boolean isDownPayment) {
+            final BigDecimal totalCredits, final boolean isDownPayment, final 
BigDecimal totalAccruedInterest) {
         this.period = periodNumber;
         this.fromDate = fromDate;
         this.dueDate = dueDate;
@@ -385,6 +391,7 @@ public final class LoanSchedulePeriodData {
         this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? 
this.totalOutstandingForPeriod : null;
         this.totalCredits = totalCredits;
         this.downPaymentPeriod = isDownPayment;
+        this.totalAccruedInterest = totalAccruedInterest;
     }
 
     private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) 
{
@@ -482,4 +489,5 @@ public final class LoanSchedulePeriodData {
     public BigDecimal totalOutstandingForPeriod() {
         return defaultToZeroIfNull(this.totalOutstandingForPeriod);
     }
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanBusinessEventSerializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanBusinessEventSerializer.java
index 6844fe235..e74cbf3c8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanBusinessEventSerializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanBusinessEventSerializer.java
@@ -73,7 +73,8 @@ public class LoanBusinessEventSerializer implements 
BusinessEventSerializer {
 
         if (data.getSummary() != null) {
             final Collection<LoanTransactionData> currentLoanTransactions = 
service.retrieveLoanTransactions(loanId);
-            
data.setSummary(LoanSummaryData.withTransactionAmountsSummary(data.getSummary(),
 currentLoanTransactions));
+            data.setSummary(
+                    
LoanSummaryData.withTransactionAmountsSummary(data.getSummary(), 
currentLoanTransactions, data.getRepaymentSchedule()));
         } else {
             
data.setSummary(LoanSummaryData.withOnlyCurrencyData(data.getCurrency()));
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index 8fd7316cb..468086871 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -1114,8 +1114,8 @@ public class LoansApiResource {
 
         // updating summary with transaction amounts summary
         if (loanBasicDetails.getSummary() != null) {
-            loanBasicDetails
-                    
.setSummary(LoanSummaryData.withTransactionAmountsSummary(loanBasicDetails.getSummary(),
 currentLoanRepayments));
+            loanBasicDetails.setSummary(
+                    
LoanSummaryData.withTransactionAmountsSummary(loanBasicDetails.getSummary(), 
currentLoanRepayments, repaymentSchedule));
         }
 
         final LoanAccountData loanAccount = 
LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, 
loanRepayments,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index 7808e0758..943303a86 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -636,7 +636,13 @@ final class LoansApiResourceSwagger {
             @Schema(example = "0.000000")
             public Double totalRepaymentTransaction;
             @Schema(example = "0.000000")
+            public Double totalInterestPaymentWaiver;
+            @Schema(example = "0.000000")
             public Double totalRepaymentTransactionReversed;
+            @Schema(example = "0.000000")
+            public Double totalUnpaidAccruedDueInterest;
+            @Schema(example = "0.000000")
+            public Double totalUnpaidAccruedNotDueInterest;
             public Set<GetLoansLoanIdOverdueCharges> overdueCharges;
             @Schema(example = "1")
             public Long chargeOffReasonId;
@@ -1004,6 +1010,14 @@ final class LoansApiResourceSwagger {
             public LocalDate delinquentDate;
             @Schema(example = "100.000000")
             public Double delinquentAmount;
+            @Schema(example = "80.000000")
+            public Double delinquentPrincipal;
+            @Schema(example = "10.000000")
+            public Double delinquentInterest;
+            @Schema(example = "6.000000")
+            public Double delinquentFee;
+            @Schema(example = "4.000000")
+            public Double delinquentPenalty;
             @Schema(example = "[2022, 07, 01]")
             public LocalDate lastPaymentDate;
             @Schema(example = "100.000000")
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 1f13036e4..2195253dd 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -1160,8 +1160,8 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                     + " ls.fee_charges_amount as feeChargesDue, 
ls.fee_charges_completed_derived as feeChargesPaid, 
ls.fee_charges_waived_derived as feeChargesWaived, 
ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
                     + " ls.penalty_charges_amount as penaltyChargesDue, 
ls.penalty_charges_completed_derived as penaltyChargesPaid, 
ls.penalty_charges_waived_derived as penaltyChargesWaived, "
                     + " ls.penalty_charges_writtenoff_derived as 
penaltyChargesWrittenOff, ls.total_paid_in_advance_derived as 
totalPaidInAdvanceForPeriod, "
-                    + " ls.total_paid_late_derived as totalPaidLateForPeriod, 
ls.credits_amount as principalCredits, ls.credited_fee as feeCredits, 
ls.credited_penalty as penaltyCredits, ls.is_down_payment isDownPayment "
-                    + " from m_loan_repayment_schedule ls ";
+                    + " ls.total_paid_late_derived as totalPaidLateForPeriod, 
ls.credits_amount as principalCredits, ls.credited_fee as feeCredits, 
ls.credited_penalty as penaltyCredits, ls.is_down_payment isDownPayment, "
+                    + " ls.accrual_interest_derived as accrualInterest " + " 
from m_loan_repayment_schedule ls ";
         }
 
         @Override
@@ -1260,6 +1260,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 final BigDecimal interestWrittenOff = 
JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
                 final BigDecimal totalInstallmentAmount = 
totalPrincipalPaid.zero().plus(principalDue).plus(interestExpectedDue)
                         .getAmount();
+                final BigDecimal accrualInterest = 
JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accrualInterest");
 
                 final BigDecimal interestActualDue = 
interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff);
                 final BigDecimal interestOutstanding = 
interestActualDue.subtract(interestPaid);
@@ -1331,7 +1332,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                         penaltyChargesPaid, penaltyChargesWaived, 
penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod,
                         totalPaidForPeriod, totalPaidInAdvanceForPeriod, 
totalPaidLateForPeriod, totalWaivedForPeriod,
                         totalWrittenOffForPeriod, totalOutstandingForPeriod, 
totalActualCostOfLoanForPeriod, totalInstallmentAmount,
-                        credits, isDownPayment);
+                        credits, isDownPayment, accrualInterest);
 
                 periods.add(periodData);
             }
@@ -2313,6 +2314,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
             final BigDecimal totalPaid = null;
             final BigDecimal totalInstallmentAmount = null;
             final BigDecimal totalCredits = null;
+            final BigDecimal totalAccruedInterest = null;
 
             return LoanSchedulePeriodData.periodWithPayments(loanId, period, 
fromDate, dueDate, obligationsMetOnDate, complete,
                     principalOriginalDue, principalPaid, principalWrittenOff, 
principalOutstanding, outstandingPrincipalBalanceOfLoan,
@@ -2320,7 +2322,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                     feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, 
feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
                     penaltyChargesWaived, penaltyChargesWrittenOff, 
penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
                     totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, 
totalWaived, totalWrittenOff, totalOutstanding,
-                    totalActualCostOfLoanForPeriod, totalInstallmentAmount, 
totalCredits, false);
+                    totalActualCostOfLoanForPeriod, totalInstallmentAmount, 
totalCredits, false, totalAccruedInterest);
         }
     }
 
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
index 0a9945675..7524a9248 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
@@ -167,8 +167,9 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LocalDate overDueSinceDate = 
DateUtils.getBusinessLocalDate().minusDays(2);
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
-        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null, null, null);
+        final BigDecimal zero = BigDecimal.ZERO;
+        CollectionData collectionData = new CollectionData(zero, 2L, null, 2L, 
overDueSinceDate, zero, null, null, null, null, null, null,
+                zero, zero, zero, zero);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
 
@@ -221,11 +222,11 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LocalDate overDueSinceDate = 
DateUtils.getBusinessLocalDate().minusDays(2);
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
-        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null, null, null);
+        CollectionData collectionData = new CollectionData(zeroAmount, 2L, 
null, 2L, overDueSinceDate, zeroAmount, null, null, null, null,
+                null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
 
-        CollectionData installmentCollectionData = new 
CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate,
-                installmentPrincipalAmount, null, null, null, null, null, 
null);
+        CollectionData installmentCollectionData = new 
CollectionData(zeroAmount, 2L, null, 2L, overDueSinceDate,
+                installmentPrincipalAmount, null, null, null, null, null, 
null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
         installmentsCollection.put(1L, installmentCollectionData);
@@ -349,11 +350,12 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LocalDate overDueSinceDate = 
DateUtils.getBusinessLocalDate().minusDays(2);
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
-        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null, null, null);
 
-        CollectionData installmentCollectionData = new 
CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate,
-                installmentPrincipalAmount, null, null, null, null, null, 
null);
+        CollectionData collectionData = new CollectionData(zeroAmount, 2L, 
null, 2L, overDueSinceDate, zeroAmount, null, null, null, null,
+                null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+
+        CollectionData installmentCollectionData = new 
CollectionData(zeroAmount, 2L, null, 2L, overDueSinceDate,
+                installmentPrincipalAmount, null, null, null, null, null, 
null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
         installmentsCollection.put(1L, installmentCollectionData);
@@ -426,11 +428,11 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LocalDate overDueSinceDate = 
DateUtils.getBusinessLocalDate().minusDays(29);
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
-        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
29L, null, 29L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null, null, null);
+        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
29L, null, 29L, overDueSinceDate, zeroAmount, null, null, null,
+                null, null, null, zeroAmount, zeroAmount, zeroAmount, 
zeroAmount);
 
-        CollectionData installmentCollectionData = new 
CollectionData(BigDecimal.ZERO, 29L, null, 29L, overDueSinceDate,
-                installmentPrincipalAmount, null, null, null, null, null, 
null);
+        CollectionData installmentCollectionData = new 
CollectionData(zeroAmount, 29L, null, 29L, overDueSinceDate,
+                installmentPrincipalAmount, null, null, null, null, null, 
null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
         installmentsCollection.put(1L, installmentCollectionData);
@@ -514,14 +516,14 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LocalDate overDueSinceDate = 
DateUtils.getBusinessLocalDate().minusDays(29);
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
                 loanForProcessing);
-        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
29L, null, 29L, overDueSinceDate, BigDecimal.ZERO, null, null,
-                null, null, null, null);
+        CollectionData collectionData = new CollectionData(zeroAmount, 29L, 
null, 29L, overDueSinceDate, zeroAmount, null, null, null, null,
+                null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
 
-        CollectionData installmentCollectionData_1 = new 
CollectionData(BigDecimal.ZERO, 29L, null, 29L, overDueSinceDate,
-                installmentPrincipalAmount, null, null, null, null, null, 
null);
+        CollectionData installmentCollectionData_1 = new 
CollectionData(zeroAmount, 29L, null, 29L, overDueSinceDate,
+                installmentPrincipalAmount, null, null, null, null, null, 
null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
 
-        CollectionData installmentCollectionData_2 = new 
CollectionData(BigDecimal.ZERO, 0L, null, 0L, null, installmentPrincipalAmount,
-                null, null, null, null, null, null);
+        CollectionData installmentCollectionData_2 = new 
CollectionData(zeroAmount, 0L, null, 0L, null, installmentPrincipalAmount, null,
+                null, null, null, null, null, zeroAmount, zeroAmount, 
zeroAmount, zeroAmount);
 
         Map<Long, CollectionData> installmentsCollection = new HashMap<>();
         installmentsCollection.put(1L, installmentCollectionData_1);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
index 5d08ad104..aa23b5072 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
@@ -70,6 +70,7 @@ import 
org.apache.fineract.integrationtests.common.LoanRescheduleRequestHelper;
 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.PeriodicAccrualAccountingHelper;
 import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
@@ -4601,6 +4602,85 @@ public class 
AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
         });
     }
 
+    // UC144: Advanced payment allocation with Interest,
+    // ADVANCED_PAYMENT_ALLOCATION_STRATEGY
+    // 1. Create a Loan product with Adv. Pment. Alloc. and with 5% Interest, 
360/30, 1 repayment per month
+    // 2. Submit Loan and approve
+    // 3. Disburse
+    // 4. Validate Repayment Schedule
+    @Test
+    public void uc144() {
+        final String operationDate = "1 January 2024";
+        AtomicLong createdLoanId = new AtomicLong();
+        runAt(operationDate, () -> {
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            PostLoanProductsRequest product = 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
+                    
.interestRatePerPeriod(12.3).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS)
+                    
.daysInMonthType(DaysInMonthType.ACTUAL.getValue()).daysInYearType(DaysInYearType.DAYS_365.getValue())
+                    .numberOfRepayments(4)//
+                    .repaymentEvery(1)//
+                    .repaymentFrequencyType(2L)//
+                    .allowPartialPeriodInterestCalcualtion(false)//
+                    .multiDisburseLoan(false)//
+                    .disallowExpectedDisbursements(null)//
+                    .allowApprovedDisbursedAmountsOverApplied(null)//
+                    .overAppliedCalculationType(null)//
+                    .overAppliedNumber(null)//
+                    .installmentAmountInMultiplesOf(null)//
+            ;//
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(product);
+            PostLoansRequest applicationRequest = applyLoanRequest(clientId, 
loanProductResponse.getResourceId(), operationDate, 100.0, 5);
+
+            applicationRequest = 
applicationRequest.numberOfRepayments(5).loanTermFrequency(5).loanTermFrequencyType(2)
+                    
.interestRatePerPeriod(BigDecimal.valueOf(12.3)).interestCalculationPeriodType(DAYS)
+                    
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY).repaymentEvery(1)
+                    .repaymentFrequencyType(2);
+
+            PostLoansResponse loanResponse = 
loanTransactionHelper.applyLoan(applicationRequest);
+            createdLoanId.set(loanResponse.getLoanId());
+
+            loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new 
PostLoansLoanIdRequest()
+                    
.approvedLoanAmount(BigDecimal.valueOf(100)).dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en"));
+
+            loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new 
PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
+                    
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
+
+            // After Disbursement we are expecting no Accrual transactions
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            assertEquals(0.0, 
loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
+            assertEquals(0.0, 
loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
+        });
+
+        // Not Due yet
+        runAt("30 January 2024", () -> {
+            // Generate the Accruals
+            final PeriodicAccrualAccountingHelper 
periodicAccrualAccountingHelper = new 
PeriodicAccrualAccountingHelper(requestSpec,
+                    responseSpec);
+            periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("30 
January 2024");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(createdLoanId.get());
+            assertEquals(0.0, 
loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
+            assertEquals(0.97, 
loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
+
+            // Partial interest repayment
+            addRepaymentForLoan(createdLoanId.get(), 20.50, "30 January 2024");
+            loanDetails = 
loanTransactionHelper.getLoanDetails(createdLoanId.get());
+            assertEquals(0.0, 
loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
+            assertEquals(0.05, 
loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
+        });
+
+        // Not Due and Due Interest
+        runAt("20 February 2024", () -> {
+            final PeriodicAccrualAccountingHelper 
periodicAccrualAccountingHelper = new 
PeriodicAccrualAccountingHelper(requestSpec,
+                    responseSpec);
+            periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("20 
February 2024");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(createdLoanId.get());
+            assertEquals(0.12, 
loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
+            assertEquals(0.52, 
loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
+        });
+    }
+
     private Long 
applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long
 clientId, Long loanProductId,
             Integer numberOfRepayments, String loanDisbursementDate, double 
amount) {
         LOG.info("------------------------------APPLY AND APPROVE LOAN 
---------------------------------------");
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
index 88c3b484c..c9cdd2456 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.integrationtests;
 
+import static 
org.apache.fineract.integrationtests.BaseLoanIntegrationTest.RepaymentFrequencyType.DAYS;
 import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
 import static 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -55,7 +56,11 @@ import 
org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
 import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
 import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+import 
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
+import org.apache.fineract.client.models.PostLoansRequest;
+import org.apache.fineract.client.models.PostLoansResponse;
 import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
 import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
 import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
@@ -1444,6 +1449,115 @@ public class DelinquencyBucketsIntegrationTest extends 
BaseLoanIntegrationTest {
         }
     }
 
+    @Test
+    public void testLoanDelinquencyDataWithAmountPerPortions() {
+        // Given
+        final LoanTransactionHelper loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        // First Range
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = 
DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        GetDelinquencyRangesResponse range = 
DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse.getResourceId());
+
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+        assertNotNull(delinquencyBucketResponse);
+        final GetDelinquencyBucketsResponse delinquencyBucket = 
DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // Client and Loan account creation
+        final Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProduct(loanTransactionHelper,
+                Math.toIntExact(delinquencyBucket.getId()), null);
+        assertNotNull(getLoanProductsProductResponse);
+        log.info("Loan Product Bucket Name: {}", 
getLoanProductsProductResponse.getDelinquencyBucket().getName());
+        
assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), 
delinquencyBucket.getName());
+
+        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
+        log.info("Local date of Tenant: {}", todaysDate);
+
+        // Older date to have more than one overdue installment
+        final LocalDate transactionDate = todaysDate.minusDays(50);
+        final String operationDate = 
Utils.dateFormatter.format(transactionDate);
+
+        final Double amount = 2000.0;
+        PostLoansRequest applicationRequest = applyLoanRequest(clientId, 
getLoanProductsProductResponse.getId(), operationDate, amount, 4);
+
+        applicationRequest = 
applicationRequest.numberOfRepayments(5).loanTermFrequency(5).loanTermFrequencyType(2)
+                
.interestRatePerPeriod(BigDecimal.valueOf(12.3)).interestCalculationPeriodType(DAYS).repaymentEvery(1)
+                .repaymentFrequencyType(2);
+
+        PostLoansResponse loanResponse = 
loanTransactionHelper.applyLoan(applicationRequest);
+        final Long loanId = loanResponse.getResourceId();
+
+        loanTransactionHelper.approveLoan(loanId, new 
PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(amount))
+                
.dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en"));
+
+        loanTransactionHelper.disburseLoan(loanId, new 
PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
+                
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(amount)).locale("en"));
+
+        GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+        log.info("Loan Delinquency Range after Disbursement {}", 
loanDetails.getDelinquencyRange().getClassification());
+        assertNotNull(loanDetails.getDelinquent());
+        log.info("Loan Delinquency Data {} {}", 
loanDetails.getDelinquent().getDelinquentPrincipal(),
+                loanDetails.getDelinquent().getDelinquentInterest());
+        assertNotNull(loanDetails.getDelinquent().getDelinquentPrincipal());
+        assertEquals(305.91, 
loanDetails.getDelinquent().getDelinquentPrincipal());
+        assertNotNull(loanDetails.getDelinquent().getDelinquentInterest());
+        assertEquals(250.72, 
loanDetails.getDelinquent().getDelinquentInterest());
+
+        // Apply a partial repayment to move only the interest
+        PostLoansLoanIdTransactionsResponse loansLoanIdTransactions = 
loanTransactionHelper.makeLoanRepayment(operationDate, 120f,
+                loanId.intValue());
+        assertNotNull(loansLoanIdTransactions);
+        log.info("Loan repayment transaction id {}", 
loansLoanIdTransactions.getResourceId());
+
+        loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+        assertNotNull(loanDetails.getDelinquent());
+        assertNotNull(loanDetails.getDelinquencyRange().getClassification());
+        assertEquals(305.91, 
loanDetails.getDelinquent().getDelinquentPrincipal());
+        assertEquals(130.72, 
loanDetails.getDelinquent().getDelinquentInterest());
+
+        // Apply a repayment to cover interest and part of the principal
+        loansLoanIdTransactions = 
loanTransactionHelper.makeLoanRepayment(operationDate, 330.72f, 
loanId.intValue());
+        assertNotNull(loansLoanIdTransactions);
+        log.info("Loan repayment transaction id {}", 
loansLoanIdTransactions.getResourceId());
+
+        loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+        assertNotNull(loanDetails.getDelinquent());
+        assertNotNull(loanDetails.getDelinquencyRange().getClassification());
+        assertEquals(105.91, 
loanDetails.getDelinquent().getDelinquentPrincipal());
+        assertEquals(0.0, loanDetails.getDelinquent().getDelinquentInterest());
+
+        // Apply a repayment to cover the remain principal
+        loansLoanIdTransactions = 
loanTransactionHelper.makeLoanRepayment(operationDate, 105.91f, 
loanId.intValue());
+        assertNotNull(loansLoanIdTransactions);
+        log.info("Loan repayment transaction id {}", 
loansLoanIdTransactions.getResourceId());
+        // Loan without Delinquency Classification
+        loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+        assertNotNull(loanDetails.getDelinquent());
+        assertNull(loanDetails.getDelinquencyRange());
+        assertEquals(0.0, 
loanDetails.getDelinquent().getDelinquentPrincipal());
+        assertEquals(0.0, loanDetails.getDelinquent().getDelinquentInterest());
+
+        // Undo the last repayment transaction we must to have pending the 
principal
+        PostLoansLoanIdTransactionsResponse reverseRepayment = 
loanTransactionHelper.reverseLoanTransaction(loanId,
+                loansLoanIdTransactions.getResourceId(), new 
PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat("dd MMMM yyyy")
+                        
.transactionDate(operationDate).transactionAmount(0.0).locale("en"));
+        assertNotNull(reverseRepayment);
+        loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+        assertNotNull(loanDetails.getDelinquent());
+        assertNotNull(loanDetails.getDelinquencyRange().getClassification());
+        assertEquals(105.91, 
loanDetails.getDelinquent().getDelinquentPrincipal());
+        assertEquals(0.0, loanDetails.getDelinquent().getDelinquentInterest());
+    }
+
     private GetLoanProductsProductIdResponse createLoanProduct(final 
LoanTransactionHelper loanTransactionHelper,
             final Integer delinquencyBucketId, final String 
inArrearsTolerance) {
         final HashMap<String, Object> loanProductMap = new 
LoanProductTestBuilder().withInArrearsTolerance(inArrearsTolerance).build(null,

Reply via email to