You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2022/11/02 14:37:45 UTC

[fineract] branch develop updated: Delinquency classification including Chargeback transactions

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

arnold 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 90f1eccd4 Delinquency classification including Chargeback transactions
90f1eccd4 is described below

commit 90f1eccd4b70d9035c90055fc07126998c99d758
Author: Jose Alberto Hernandez <al...@MacBook-Pro.local>
AuthorDate: Thu Oct 13 13:31:40 2022 -0500

    Delinquency classification including Chargeback transactions
---
 ...etailsReadPlatformServiceJpaRepositoryImpl.java |   7 +-
 .../service/DelinquencyReadPlatformService.java    |   3 +
 .../DelinquencyReadPlatformServiceImpl.java        | 100 +++++
 .../service/DelinquencyWritePlatformService.java   |  12 +-
 .../DelinquencyWritePlatformServiceImpl.java       | 125 ++++--
 .../loanaccount/api/LoansApiResource.java          |   4 +-
 .../portfolio/loanaccount/data/CollectionData.java |  38 +-
 .../loanaccount/data/LoanAccountData.java          |   6 +
 .../data/LoanScheduleDelinquencyData.java          |  13 +-
 .../portfolio/loanaccount/domain/Loan.java         |  75 ++--
 .../domain/LoanAccountDomainServiceJpa.java        |  15 +-
 .../domain/LoanRepaymentScheduleInstallment.java   |   6 +-
 ...LoanRepaymentScheduleInstallmentRepository.java |  39 +-
 .../domain/LoanTransactionRepository.java          |  19 +
 ...ctionToRepaymentScheduleMappingRepository.java} |   7 +-
 ...tLoanRepaymentScheduleTransactionProcessor.java |   2 +-
 .../SetLoanDelinquencyTagsConfig.java              |   9 +-
 .../SetLoanDelinquencyTagsTasklet.java             |  62 ++-
 .../domain/AbstractLoanScheduleGenerator.java      |   2 +-
 .../service/LoanReadPlatformService.java           |   6 -
 .../service/LoanReadPlatformServiceImpl.java       | 123 +-----
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |   2 +-
 .../savings/domain/FixedDepositAccount.java        |   4 +-
 .../savings/domain/RecurringDepositAccount.java    |   6 +-
 .../portfolio/savings/domain/SavingsAccount.java   |  20 +-
 .../SavingsAccountInterestPostingServiceImpl.java  |   8 +-
 .../db/changelog/tenant/changelog-tenant.xml       |   1 +
 .../0066_delinquency_classification_chargeback.xml |  33 ++
 .../DelinquencyAndChargebackIntegrationTest.java   | 192 ++++++++++
 .../DelinquencyBucketsIntegrationTest.java         | 421 ++++++++++-----------
 .../LoanTransactionChargebackTest.java             | 130 ++++---
 .../common/loans/LoanTransactionHelper.java        |  57 ++-
 .../common/products/DelinquencyBucketsHelper.java  |  66 +++-
 .../common/products/DelinquencyRangesHelper.java   |   2 +-
 34 files changed, 1061 insertions(+), 554 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java
index e783e7bfb..ff1f30313 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java
@@ -478,7 +478,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
 
                     .append(" l.closedon_date as closedOnDate,")
                     .append(" cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,")
-                    .append(" la.overdue_since_date_derived as overdueSinceDate,")
+                    .append(" la.overdue_since_date_derived as overdueSinceDate, ")
                     .append(" l.writtenoffon_date as writtenOffOnDate, l.expected_maturedon_date as expectedMaturityDate")
 
                     .append(" from m_loan l ").append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id")
@@ -550,10 +550,7 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
             final LocalDate expectedMaturityDate = JdbcSupport.getLocalDate(rs, "expectedMaturityDate");
 
             final LocalDate overdueSinceDate = JdbcSupport.getLocalDate(rs, "overdueSinceDate");
-            Boolean inArrears = true;
-            if (overdueSinceDate == null) {
-                inArrears = false;
-            }
+            Boolean inArrears = (overdueSinceDate != null);
 
             final LoanApplicationTimelineData timeline = new LoanApplicationTimelineData(submittedOnDate, submittedByUsername,
                     submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname,
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java
index 9206c615f..b1c2dad11 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformService.java
@@ -22,6 +22,7 @@ import java.util.Collection;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
 import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
+import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 
 public interface DelinquencyReadPlatformService {
 
@@ -37,4 +38,6 @@ public interface DelinquencyReadPlatformService {
 
     Collection<LoanDelinquencyTagHistoryData> retrieveDelinquencyRangeHistory(Long loanId);
 
+    CollectionData calculateLoanCollectionData(Long loanId);
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
index 023970b39..e1585d9d5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
@@ -18,10 +18,15 @@
  */
 package org.apache.fineract.portfolio.delinquency.service;
 
+import java.math.BigDecimal;
+import java.time.LocalDate;
 import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
 import org.apache.fineract.portfolio.delinquency.data.LoanDelinquencyTagHistoryData;
@@ -34,8 +39,13 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistor
 import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
 import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
 import org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper;
+import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -97,4 +107,94 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo
         return mapperLoanDelinquencyTagHistory.map(loanDelinquencyTagData);
     }
 
+    @Override
+    public CollectionData calculateLoanCollectionData(final Long loanId) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        final Optional<Loan> optLoan = this.loanRepository.findById(loanId);
+
+        CollectionData collectionData = CollectionData.template();
+        if (optLoan.isPresent()) {
+            final Loan loan = optLoan.get();
+
+            collectionData.setAvailableDisbursementAmount(loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()));
+            collectionData.setNextPaymentDueDate(loan.possibleNextRepaymentDate());
+
+            final MonetaryCurrency currency = loan.getCurrency();
+            BigDecimal delinquentAmount = BigDecimal.ZERO;
+            // Overdue Days calculation
+            Long overdueDays = 0L;
+            LocalDate overdueSinceDate = null;
+            final List<LoanTransaction> chargebackTransactions = loan.retrieveListOfTransactionsByType(LoanTransactionType.CHARGEBACK);
+            for (LoanTransaction loanTransaction : chargebackTransactions) {
+                Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = loanTransaction
+                        .getLoanTransactionToRepaymentScheduleMappings();
+                LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = loanTransactionToRepaymentScheduleMappings
+                        .iterator().next();
+                if (!loanTransactionToRepaymentScheduleMapping.getLoanRepaymentScheduleInstallment().isPrincipalCompleted(currency)) {
+                    overdueSinceDate = loanTransaction.getTransactionDate();
+                    break;
+                }
+            }
+
+            final List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
+            if (overdueSinceDate == null) {
+                for (LoanRepaymentScheduleInstallment installment : installments) {
+                    if (installment.isNotFullyPaidOff()) {
+                        overdueSinceDate = installment.getDueDate();
+                        break;
+                    }
+                }
+            }
+
+            Integer graceDays = 0;
+            if (overdueSinceDate != null) {
+                if (loan.loanProduct().getLoanProductConfigurableAttributes().getGraceOnArrearsAgingBoolean()) {
+                    graceDays = loan.loanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
+                    if (graceDays == null) {
+                        graceDays = 0;
+                    }
+                    overdueSinceDate = overdueSinceDate.plusDays(graceDays);
+                }
+                overdueDays = DateUtils.getDifferenceInDays(overdueSinceDate, businessDate);
+                if (overdueDays < 0) {
+                    overdueDays = 0L;
+                }
+                collectionData.setDelinquentDate(overdueSinceDate);
+            }
+
+            collectionData.setPastDueDays(overdueDays);
+            if (overdueDays > 0) {
+                collectionData.setDelinquentDays(overdueDays - graceDays);
+            }
+
+            final LoanTransaction lastRepaymenTransaction = loan.getLastRepaymentTransaction();
+            if (lastRepaymenTransaction != null) {
+                collectionData.setLastPaymentDate(lastRepaymenTransaction.getTransactionDate());
+                collectionData.setLastPaymentAmount(lastRepaymenTransaction.getAmount());
+            }
+
+            // Calculate Delinquency Amount
+            for (LoanRepaymentScheduleInstallment installment : installments) {
+                if (installment.getDueDate().isBefore(businessDate)) {
+                    delinquentAmount = delinquentAmount.add(installment.getTotalOutstanding(currency).getAmount());
+                }
+            }
+            for (LoanTransaction loanTransaction : chargebackTransactions) {
+                Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = loanTransaction
+                        .getLoanTransactionToRepaymentScheduleMappings();
+                for (LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping : loanTransactionToRepaymentScheduleMappings) {
+                    LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loanTransactionToRepaymentScheduleMapping
+                            .getLoanRepaymentScheduleInstallment();
+                    if (!loanRepaymentScheduleInstallment.isPrincipalCompleted(currency)) {
+                        delinquentAmount = delinquentAmount.add(loanTransaction.getAmount());
+                    }
+                }
+            }
+
+            collectionData.setDelinquentAmount(delinquentAmount);
+        }
+
+        return collectionData;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java
index 8795ace83..d5596ce72 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformService.java
@@ -18,8 +18,10 @@
  */
 package org.apache.fineract.portfolio.delinquency.service;
 
+import java.time.LocalDate;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 
 public interface DelinquencyWritePlatformService {
@@ -38,12 +40,14 @@ public interface DelinquencyWritePlatformService {
 
     CommandProcessingResult applyDelinquencyTagToLoan(Long loanId, JsonCommand command);
 
-    void applyDelinquencyTagToLoan(Long loanId, Long ageDays);
-
-    void applyDelinquencyTagToLoan(Loan loan, Long ageDays);
-
     void removeDelinquencyTagToLoan(Loan loan);
 
     void cleanLoanDelinquencyTags(Loan loan);
 
+    LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData);
+
+    void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData);
+
+    LocalDate getOverdueSinceDate(Loan loan, LocalDate businessDate, Integer graceOnArrearAgeing);
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
index 4d1dc384c..b1d543a7d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
@@ -33,6 +33,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
 import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
 import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
@@ -48,10 +49,13 @@ import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketAges
 import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeInvalidAgesException;
 import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
 import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
+import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
 import org.springframework.stereotype.Service;
 
@@ -69,7 +73,6 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
     private final LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository;
     private final LoanRepositoryWrapper loanRepository;
     private final LoanProductRepository loanProductRepository;
-    private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
 
     @Override
     public CommandProcessingResult createDelinquencyRange(JsonCommand command) {
@@ -100,8 +103,9 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
                         "Data integrity issue with resource: " + delinquencyRange.getId());
             }
             repositoryRange.delete(delinquencyRange);
+            return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
         }
-        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRange.getId()).build();
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyRangeId).build();
     }
 
     @Override
@@ -135,7 +139,71 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
             }
             repositoryBucket.delete(delinquencyBucket);
         }
-        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucket.getId()).build();
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(delinquencyBucketId).build();
+    }
+
+    @Override
+    public LoanScheduleDelinquencyData calculateDelinquencyData(LoanScheduleDelinquencyData loanScheduleDelinquencyData) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        Loan loan = loanScheduleDelinquencyData.getLoan();
+        if (loan == null) {
+            loan = this.loanRepository.findOneWithNotFoundDetection(loanScheduleDelinquencyData.getLoanId());
+        }
+
+        final Integer graceOnArrearAgeing = loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
+        final LocalDate overdueSinceDate = getOverdueSinceDate(loan, businessDate, graceOnArrearAgeing);
+        final Long overdueDays = calculateOverdueDays(businessDate, overdueSinceDate);
+        return new LoanScheduleDelinquencyData(loan.getId(), overdueSinceDate, overdueDays, loan);
+    }
+
+    @Override
+    public LocalDate getOverdueSinceDate(final Loan loan, final LocalDate businessDate, final Integer graceOnArrearAgeing) {
+        LoanRepaymentScheduleInstallment loanRepaymentSchedule = null;
+        for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
+            if (installment.getDueDate().isBefore(businessDate) && installment.isNotFullyPaidOff()) {
+                loanRepaymentSchedule = installment;
+                break;
+            }
+        }
+
+        LocalDate overdueSinceDate = null;
+        final MonetaryCurrency loanCurrency = loan.getCurrency();
+        final List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsByType(LoanTransactionType.CHARGEBACK);
+        if (loanRepaymentSchedule != null) {
+            // Default Due date
+            overdueSinceDate = loanRepaymentSchedule.getDueDate();
+        }
+
+        // If there is some Loan Transaction Chargeback
+        for (LoanTransaction loanTransaction : loanTransactions) {
+            if (loanTransaction.getLoanTransactionToRepaymentScheduleMappings().iterator().hasNext()) {
+                final LoanTransactionToRepaymentScheduleMapping transactionMapping = loanTransaction
+                        .getLoanTransactionToRepaymentScheduleMappings().iterator().next();
+
+                if (transactionMapping != null
+                        && !transactionMapping.getLoanRepaymentScheduleInstallment().isPrincipalCompleted(loanCurrency)) {
+                    overdueSinceDate = loanTransaction.getTransactionDate();
+                    break;
+                }
+            }
+        }
+
+        // Include grace days If It's defined
+        if (overdueSinceDate != null && graceOnArrearAgeing != null) {
+            overdueSinceDate = overdueSinceDate.plusDays(graceOnArrearAgeing.longValue());
+        }
+        return overdueSinceDate;
+    }
+
+    private Long calculateOverdueDays(final LocalDate businessDate, LocalDate overdueSinceDate) {
+        Long overdueDays = 0L;
+        if (overdueSinceDate != null) {
+            overdueDays = DateUtils.getDifferenceInDays(overdueSinceDate, businessDate);
+            if (overdueDays < 0) {
+                overdueDays = 0L;
+            }
+        }
+        return overdueDays;
     }
 
     @Override
@@ -146,28 +214,31 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
         final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
         if (delinquencyBucket != null) {
             final LocalDate businessDate = DateUtils.getBusinessLocalDate();
-            LoanRepaymentScheduleInstallment loanRepaymentSchedule = this.loanRepaymentScheduleInstallmentRepository
-                    .findFirstByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(loan, businessDate, null);
 
-            Long overdueDays = 0L;
-            if (loanRepaymentSchedule != null) {
-                overdueDays = DateUtils.getDifferenceInDays(loanRepaymentSchedule.getDueDate(), businessDate);
-            }
+            final Integer graceOnArrearAgeing = loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
+            final LocalDate overdueSinceDate = getOverdueSinceDate(loan, businessDate, graceOnArrearAgeing);
+
+            final Long overdueDays = calculateOverdueDays(businessDate, overdueSinceDate);
             changes = lookUpDelinquencyRange(loan, delinquencyBucket, overdueDays);
         }
         return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loan.getId()).with(changes).build();
     }
 
     @Override
-    public void applyDelinquencyTagToLoan(Long loanId, Long ageDays) {
-        final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId);
-        applyDelinquencyTagToLoan(loan, ageDays);
-    }
-
-    @Override
-    public void applyDelinquencyTagToLoan(final Loan loan, Long ageDays) {
+    public void applyDelinquencyTagToLoan(LoanScheduleDelinquencyData loanDelinquencyData) {
+        final Loan loan = loanDelinquencyData.getLoan();
         if (loan.hasDelinquencyBucket()) {
-            lookUpDelinquencyRange(loan, loan.getLoanProduct().getDelinquencyBucket(), ageDays);
+            final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+            final DelinquencyBucket delinquencyBucket = loan.getLoanProduct().getDelinquencyBucket();
+            final Integer graceOnArrearAgeing = loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
+
+            LocalDate overdueSinceDate = loanDelinquencyData.getOverdueSinceDate();
+            if (overdueSinceDate == null) {
+                getOverdueSinceDate(loan, businessDate, graceOnArrearAgeing);
+            }
+
+            final Long overdueDays = calculateOverdueDays(businessDate, overdueSinceDate);
+            lookUpDelinquencyRange(loan, delinquencyBucket, overdueDays);
         }
     }
 
@@ -289,11 +360,11 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
         }
     }
 
-    private Map<String, Object> lookUpDelinquencyRange(final Loan loan, final DelinquencyBucket delinquencyBucket, long ageDays) {
+    private Map<String, Object> lookUpDelinquencyRange(final Loan loan, final DelinquencyBucket delinquencyBucket, long overdueDays) {
         Map<String, Object> changes = new HashMap<>();
 
-        if (ageDays <= 0) { // No Delinquency
-            log.debug("Loan {} without delinquency range with {} days", loan.getId(), ageDays);
+        if (overdueDays <= 0) { // No Delinquency
+            log.debug("Loan {} without delinquency range with {} days", loan.getId(), overdueDays);
             changes = setLoanDelinquencyTag(loan, null);
 
         } else {
@@ -302,27 +373,27 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat
 
             for (final DelinquencyRange delinquencyRange : ranges) {
                 if (delinquencyRange.getMaximumAgeDays() == null) { // Last Range in the Bucket
-                    if (delinquencyRange.getMinimumAgeDays() <= ageDays) {
+                    if (delinquencyRange.getMinimumAgeDays() <= overdueDays) {
                         log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
-                                ageDays);
+                                overdueDays);
                         changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
                         break;
                     }
                 } else {
-                    if (delinquencyRange.getMinimumAgeDays() <= ageDays && delinquencyRange.getMaximumAgeDays() >= ageDays) {
+                    if (delinquencyRange.getMinimumAgeDays() <= overdueDays && delinquencyRange.getMaximumAgeDays() >= overdueDays) {
                         log.debug("Loan {} with delinquency range {} with {} days", loan.getId(), delinquencyRange.getClassification(),
-                                ageDays);
+                                overdueDays);
                         changes = setLoanDelinquencyTag(loan, delinquencyRange.getId());
                         break;
                     }
                 }
             }
         }
-        changes.put("ageDays", ageDays);
+        changes.put("overdueDays", overdueDays);
         return changes;
     }
 
-    public Map<String, Object> setLoanDelinquencyTag(Loan loan, Long delinquencyRangeId) {
+    private Map<String, Object> setLoanDelinquencyTag(Loan loan, Long delinquencyRangeId) {
         Map<String, Object> changes = new HashMap<>();
         List<LoanDelinquencyTagHistory> loanDelinquencyTagHistory = new ArrayList<>();
         final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
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 8468e2e01..95d6a082a 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
@@ -610,8 +610,8 @@ public class LoansApiResource {
 
             if (associationParameters.contains(DataTableApiConstant.collectionAssociateParamName)) {
                 mandatoryResponseParameters.add(DataTableApiConstant.collectionAssociateParamName);
-                if (LoanStatus.fromInt(loanBasicDetails.getStatus().getId().intValue()).isActive()) {
-                    collectionData = this.loanReadPlatformService.retrieveLoanCollectionData(loanId);
+                if (loanBasicDetails.isActive()) {
+                    collectionData = this.delinquencyReadPlatformService.calculateLoanCollectionData(loanId);
                 }
             }
         }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
index 37882f00e..45c2545cc 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
@@ -20,45 +20,27 @@ package org.apache.fineract.portfolio.loanaccount.data;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.Accessors;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
 
-@Data
-@NoArgsConstructor
-@Accessors(chain = true)
+@AllArgsConstructor
+@Getter
+@Setter
 public final class CollectionData {
 
     private BigDecimal availableDisbursementAmount;
-    private int pastDueDays;
+    private Long pastDueDays;
     private LocalDate nextPaymentDueDate;
-    private int delinquentDays;
+    private Long delinquentDays;
     private LocalDate delinquentDate;
     private BigDecimal delinquentAmount;
     private LocalDate lastPaymentDate;
     private BigDecimal lastPaymentAmount;
 
-    private CollectionData(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate, int delinquentDays,
-            LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate, BigDecimal lastPaymentAmount) {
-        this.availableDisbursementAmount = availableDisbursementAmount;
-        this.pastDueDays = pastDueDays;
-        this.nextPaymentDueDate = nextPaymentDueDate;
-        this.delinquentDays = delinquentDays;
-        this.delinquentDate = delinquentDate;
-        this.delinquentAmount = delinquentAmount;
-        this.lastPaymentDate = lastPaymentDate;
-        this.lastPaymentAmount = lastPaymentAmount;
-    }
-
-    public static CollectionData instance(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate,
-            int delinquentDays, LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate,
-            BigDecimal lastPaymentAmount) {
-        return new CollectionData(availableDisbursementAmount, pastDueDays, nextPaymentDueDate, delinquentDays, delinquentDate,
-                delinquentAmount, lastPaymentDate, lastPaymentAmount);
-    }
-
     public static CollectionData template() {
         final BigDecimal zero = BigDecimal.ZERO;
-        return new CollectionData(zero, 0, null, 0, null, zero, null, zero);
+        return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero);
     }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
index 98769f2bc..5b149027b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
@@ -44,6 +44,7 @@ import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData;
 import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
 import org.apache.fineract.portfolio.fund.data.FundData;
 import org.apache.fineract.portfolio.group.data.GroupGeneralData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.guarantor.data.GuarantorData;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
@@ -56,6 +57,7 @@ import org.apache.fineract.portfolio.rate.data.RateData;
 @Data
 @NoArgsConstructor
 @Accessors(chain = true)
+@SuppressWarnings("ObjectToString")
 public class LoanAccountData {
 
     // basic loan details
@@ -1167,4 +1169,8 @@ public class LoanAccountData {
         }
         return null;
     }
+
+    public boolean isActive() {
+        return LoanStatus.fromInt(getStatus().getId().intValue()).isActive();
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleDelinquencyData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleDelinquencyData.java
index 4458ae419..7d3fad62a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleDelinquencyData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleDelinquencyData.java
@@ -22,14 +22,19 @@ import java.io.Serializable;
 import java.time.LocalDate;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 
 @AllArgsConstructor
+@NoArgsConstructor
 @Getter
+@Setter
 public class LoanScheduleDelinquencyData implements Serializable {
 
-    private final Long loanId;
-    private final Long productId;
-    private final LocalDate dueDate;
-    private final Long ageDays;
+    private Long loanId;
+    private LocalDate overdueSinceDate;
+    private Long overdueDays;
+    private Loan loan;
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index ac3f0a932..6b5779d9f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -672,7 +672,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         ChangedTransactionDetail changedTransactionDetail = null;
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
-        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
         changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
                 allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
         for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -832,7 +832,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
              * Consider removing this block of code or logically completing it for the future by getting the list of
              * affected Transactions
              ***/
-            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
             loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
                     getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
         }
@@ -900,7 +900,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
              * Consider removing this block of code or logically completing it for the future by getting the list of
              * affected Transactions
              ***/
-            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
             loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
                     getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
         } else {
@@ -1087,7 +1087,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
              * Consider removing this block of code or logically completing it for the future by getting the list of
              * affected Transactions
              ***/
-            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
             loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
                     getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
         } else {
@@ -1284,7 +1284,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
      * method updates accrual derived fields on installments and reverse the unprocessed transactions
      */
     private void applyAccurals() {
-        Collection<LoanTransaction> accruals = retreiveListOfAccrualTransactions();
+        Collection<LoanTransaction> accruals = retrieveListOfAccrualTransactions();
         if (!accruals.isEmpty()) {
             if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
                 applyPeriodicAccruals(accruals);
@@ -2634,7 +2634,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
     private ChangedTransactionDetail reprocessTransactionForDisbursement() {
         ChangedTransactionDetail changedTransactionDetail = null;
         if (this.loanProduct.isMultiDisburseLoan()) {
-            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
             if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
                 final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                         .determineProcessor(this.transactionProcessingStrategyCode);
@@ -3250,7 +3250,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
             if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
                 regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
             }
-            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
             changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
                     allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
             for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -3300,7 +3300,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return installment;
     }
 
-    private List<LoanTransaction> retreiveListOfIncomePostingTransactions() {
+    private List<LoanTransaction> retrieveListOfIncomePostingTransactions() {
         final List<LoanTransaction> incomePostTransactions = new ArrayList<>();
         List<LoanTransaction> trans = getLoanTransactions();
         for (final LoanTransaction transaction : trans) {
@@ -3313,7 +3313,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return incomePostTransactions;
     }
 
-    private List<LoanTransaction> retreiveListOfTransactionsPostDisbursement() {
+    private List<LoanTransaction> retrieveListOfTransactionsPostDisbursement() {
         final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
         List<LoanTransaction> trans = getLoanTransactions();
         for (final LoanTransaction transaction : trans) {
@@ -3326,7 +3326,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return repaymentsOrWaivers;
     }
 
-    public List<LoanTransaction> retreiveListOfTransactionsPostDisbursementExcludeAccruals() {
+    public List<LoanTransaction> retrieveListOfTransactionsPostDisbursementExcludeAccruals() {
         final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
         for (final LoanTransaction transaction : this.loanTransactions) {
             if (transaction.isNotReversed()
@@ -3340,7 +3340,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return repaymentsOrWaivers;
     }
 
-    private List<LoanTransaction> retreiveListOfTransactionsExcludeAccruals() {
+    private List<LoanTransaction> retrieveListOfTransactionsExcludeAccruals() {
         final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
         for (final LoanTransaction transaction : this.loanTransactions) {
             if (transaction.isNotReversed() && !(transaction.isAccrual() || transaction.isNonMonetaryTransaction())) {
@@ -3352,7 +3352,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return repaymentsOrWaivers;
     }
 
-    private List<LoanTransaction> retreiveListOfAccrualTransactions() {
+    private List<LoanTransaction> retrieveListOfAccrualTransactions() {
         final List<LoanTransaction> transactions = new ArrayList<>();
         for (final LoanTransaction transaction : this.loanTransactions) {
             if (transaction.isNotReversed() && transaction.isAccrual()) {
@@ -3364,6 +3364,18 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return transactions;
     }
 
+    public List<LoanTransaction> retrieveListOfTransactionsByType(LoanTransactionType transactionType) {
+        final List<LoanTransaction> transactions = new ArrayList<>();
+        for (final LoanTransaction transaction : this.loanTransactions) {
+            if (transaction.isNotReversed() && transaction.getTypeOf().equals(transactionType)) {
+                transactions.add(transaction);
+            }
+        }
+        final LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
+        Collections.sort(transactions, transactionComparator);
+        return transactions;
+    }
+
     private boolean doPostLoanTransactionChecks(final LocalDate transactionDate,
             final LoanLifecycleStateMachine loanLifecycleStateMachine) {
         boolean statusChanged = false;
@@ -3403,12 +3415,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         if (this.loanInterestRecalculationDetails != null && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
                 && this.getStatus().isClosedObligationsMet()) {
             LocalDate closedDate = this.getClosedOnDate();
-            reverseTransactionsOnOrAfter(retreiveListOfIncomePostingTransactions(), closedDate);
-            reverseTransactionsOnOrAfter(retreiveListOfAccrualTransactions(), closedDate);
+            reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(), closedDate);
+            reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(), closedDate);
             HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
             determineCumulativeIncomeFromInstallments(cumulativeIncomeFromInstallments);
             HashMap<String, BigDecimal> cumulativeIncomeFromIncomePosting = new HashMap<>();
-            determineCumulativeIncomeDetails(retreiveListOfIncomePostingTransactions(), cumulativeIncomeFromIncomePosting);
+            determineCumulativeIncomeDetails(retrieveListOfIncomePostingTransactions(), cumulativeIncomeFromIncomePosting);
             BigDecimal interestToPost = cumulativeIncomeFromInstallments.get(INTEREST)
                     .subtract(cumulativeIncomeFromIncomePosting.get(INTEREST));
             BigDecimal feeToPost = cumulativeIncomeFromInstallments.get(FEE).subtract(cumulativeIncomeFromIncomePosting.get(FEE));
@@ -3419,7 +3431,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
                     interestToPost, feeToPost, penaltyToPost);
             addLoanTransaction(finalIncomeTransaction);
             if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
-                List<LoanTransaction> updatedAccrualTransactions = retreiveListOfAccrualTransactions();
+                List<LoanTransaction> updatedAccrualTransactions = retrieveListOfAccrualTransactions();
                 LocalDate lastAccruedDate = this.getDisbursementDate();
                 if (!updatedAccrualTransactions.isEmpty()) {
                     lastAccruedDate = updatedAccrualTransactions.get(updatedAccrualTransactions.size() - 1).getTransactionDate();
@@ -3646,7 +3658,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, this);
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
-        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
         if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
             regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
         }
@@ -3789,7 +3801,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
             if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
                 regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
             }
-            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
             changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
                     allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
             for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -4856,6 +4868,15 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return currentTransactionDate;
     }
 
+    public LoanTransaction getLastRepaymentTransaction() {
+        for (final LoanTransaction loanTransaction : this.loanTransactions) {
+            if (!loanTransaction.isReversed() && loanTransaction.isRepaymentType()) {
+                return loanTransaction;
+            }
+        }
+        return null;
+    }
+
     public LocalDate getLastUserTransactionForChargeCalc() {
         LocalDate lastTransaction = getDisbursementDate();
         if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
@@ -5130,7 +5151,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
 
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
-        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
         ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
                 getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
                 getActiveCharges());
@@ -5243,7 +5264,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
         /*
          * LocalDate recalculateFrom = null; List<LoanTransaction> loanTransactions =
-         * this.retreiveListOfTransactionsPostDisbursementExcludeAccruals(); for (LoanTransaction loanTransaction :
+         * this.retrieveListOfTransactionsPostDisbursementExcludeAccruals(); for (LoanTransaction loanTransaction :
          * loanTransactions) { if (recalculateFrom == null ||
          * loanTransaction.getTransactionDate().isAfter(recalculateFrom)) { recalculateFrom =
          * loanTransaction.getTransactionDate(); } } generatorDTO.setRecalculateFrom(recalculateFrom);
@@ -5266,7 +5287,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
     public ChangedTransactionDetail processTransactions() {
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
-        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
         ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
                 getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
                 getActiveCharges());
@@ -5344,8 +5365,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         if (this.loanInterestRecalculationDetails != null && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()) {
             LocalDate lastCompoundingDate = this.getDisbursementDate();
             List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = extractInterestRecalculationAdditionalDetails();
-            List<LoanTransaction> incomeTransactions = retreiveListOfIncomePostingTransactions();
-            List<LoanTransaction> accrualTransactions = retreiveListOfAccrualTransactions();
+            List<LoanTransaction> incomeTransactions = retrieveListOfIncomePostingTransactions();
+            List<LoanTransaction> accrualTransactions = retrieveListOfAccrualTransactions();
             for (LoanInterestRecalcualtionAdditionalDetails compoundingDetail : compoundingDetails) {
                 if (!compoundingDetail.getEffectiveDate().isBefore(DateUtils.getBusinessLocalDate())) {
                     break;
@@ -5495,7 +5516,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
     public void processPostDisbursementTransactions() {
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategyCode);
-        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+        final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
         final List<LoanTransaction> copyTransactions = new ArrayList<>();
         if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
             for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
@@ -5653,7 +5674,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
 
     private void updateLoanOutstandingBalances() {
         Money outstanding = Money.zero(getCurrency());
-        List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
+        List<LoanTransaction> loanTransactions = retrieveListOfTransactionsExcludeAccruals();
         for (LoanTransaction loanTransaction : loanTransactions) {
             if (loanTransaction.isDisbursement() || loanTransaction.isIncomePosting()) {
                 outstanding = outstanding.plus(loanTransaction.getAmount(getCurrency()));
@@ -6099,7 +6120,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
             loanRepaymentScheduleTransactionProcessor.handleRefund(loanTransaction, getCurrency(), getRepaymentScheduleInstallments(),
                     getActiveCharges());
         } else {
-            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
+            final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement();
             changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
                     allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
             for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -6191,7 +6212,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO_LAST, getDisbursementDate());
         LocalDate actualDisbursementDate = null;
         LocalDate lastTransactionDate = getDisbursementDate();
-        List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
+        List<LoanTransaction> loanTransactions = retrieveListOfTransactionsExcludeAccruals();
         Collections.reverse(loanTransactions);
         for (final LoanTransaction previousTransaction : loanTransactions) {
             if (lastTransactionDate.isBefore(previousTransaction.getTransactionDate())
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index 53ded9aa4..54d1989e8 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -84,6 +84,7 @@ import org.apache.fineract.portfolio.group.domain.Group;
 import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
 import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
 import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
 import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
@@ -541,13 +542,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
 
     @Override
     public void setLoanDelinquencyTag(final Loan loan, final LocalDate transactionDate) {
-        // Revalidate the Delinquency Classification
-        final Long ageOfOverdueDays = loan.getAgeOfOverdueDays(transactionDate);
-        log.debug("Loan {} with {} days and current classification {}", loan.getId(), ageOfOverdueDays);
-        if (ageOfOverdueDays > 0L) { // If loan is overdue
-            this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loan, ageOfOverdueDays);
+        LoanScheduleDelinquencyData loanDelinquencyData = new LoanScheduleDelinquencyData(loan.getId(), transactionDate, null, loan);
+        loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData);
+        log.debug("Processing Loan {} with {} overdue days since date {}", loanDelinquencyData.getLoanId(),
+                loanDelinquencyData.getOverdueDays(), loanDelinquencyData.getOverdueSinceDate());
+        // Set or Unset the Delinquency Classification Tag
+        if (loanDelinquencyData.getOverdueDays() > 0) {
+            this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData);
         } else {
-            this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loan);
+            this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loanDelinquencyData.getLoan());
         }
     }
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
index 071dcf377..444c47aeb 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
@@ -637,7 +637,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
     private void checkIfRepaymentPeriodObligationsAreMet(final LocalDate transactionDate, final MonetaryCurrency currency) {
         this.obligationsMet = getTotalOutstanding(currency).isZero();
         if (this.obligationsMet) {
-            this.obligationsMetOnDate = transactionDate;
+            this.obligationsMet = getCredits(currency).isZero();
+            if (this.obligationsMet) {
+                this.obligationsMetOnDate = transactionDate;
+            }
         } else {
             this.obligationsMetOnDate = null;
         }
@@ -704,7 +707,6 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
             this.principal = this.principal.add(transactionAmount.getAmount());
         }
         checkIfRepaymentPeriodObligationsAreMet(transactionDate, transactionAmount.getCurrency());
-
     }
 
     public void addToCredits(final BigDecimal amount) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallmentRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallmentRepository.java
index b6da7b843..a484fa551 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallmentRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallmentRepository.java
@@ -19,16 +19,47 @@
 package org.apache.fineract.portfolio.loanaccount.domain;
 
 import java.time.LocalDate;
+import java.util.Collection;
 import java.util.List;
+import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
 
 public interface LoanRepaymentScheduleInstallmentRepository
         extends JpaRepository<LoanRepaymentScheduleInstallment, Long>, JpaSpecificationExecutor<LoanRepaymentScheduleInstallment> {
 
-    LoanRepaymentScheduleInstallment findFirstByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(Loan loan,
-            LocalDate businnessDate, LocalDate obligationsMetOnDate);
+    @Query("""
+            SELECT new org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData(
+                lrs.loan.id,
+                min(lrs.dueDate),
+                0L,
+                lrs.loan
+            ) FROM LoanRepaymentScheduleInstallment lrs
+            WHERE lrs.loan.loanStatus = :loanStatus AND
+            lrs.dueDate <= :businessDate AND
+            lrs.obligationsMet = :obligationsMet AND
+            lrs.loan.loanProduct.delinquencyBucket IS NOT NULL
+            GROUP BY lrs.loan
+            """)
+    Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(Integer loanStatus, LocalDate businessDate,
+            boolean obligationsMet);
+
+    @Query("""
+            SELECT new org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData(
+                lrs.loan.id,
+                min(lrs.dueDate),
+                0L,
+                lrs.loan
+            ) FROM LoanRepaymentScheduleInstallment lrs
+            WHERE lrs.loan.loanStatus = :loanStatus AND
+            lrs.dueDate <= :businessDate AND
+            lrs.obligationsMet = :obligationsMet AND
+            lrs.loan.loanProduct.delinquencyBucket IS NOT NULL AND
+            lrs.loan.id NOT IN :loanIds
+            GROUP BY lrs.loan
+            """)
+    Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(Integer loanStatus, LocalDate businessDate,
+            boolean obligationsMet, List<Long> loanIds);
 
-    List<LoanRepaymentScheduleInstallment> findByLoanAndDueDateLessThanEqualAndObligationsMetOnDateOrderByDueDate(Loan loan,
-            LocalDate businnessDate, LocalDate obligationsMetOnDate);
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
index 16f6268cf..42429f083 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
@@ -18,11 +18,30 @@
  */
 package org.apache.fineract.portfolio.loanaccount.domain;
 
+import java.time.LocalDate;
+import java.util.Collection;
 import java.util.Optional;
+import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
 
 public interface LoanTransactionRepository extends JpaRepository<LoanTransaction, Long>, JpaSpecificationExecutor<LoanTransaction> {
 
     Optional<LoanTransaction> findByIdAndLoanId(Long transactionId, Long loanId);
+
+    @Query("""
+            SELECT new org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData(
+                lt.loan.id,
+                min(lt.dateOf),
+                0L,
+                lt.loan
+            ) FROM LoanTransaction lt
+            WHERE lt.typeOf = :transactionType and
+            lt.dateOf <= :businessDate and
+            lt.loan.loanProduct.delinquencyBucket is not null
+            GROUP BY lt.loan
+            """)
+    Collection<LoanScheduleDelinquencyData> fetchLoanTransactionsByTypeAndLessOrEqualDate(Integer transactionType, LocalDate businessDate);
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMappingRepository.java
similarity index 75%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMappingRepository.java
index 16f6268cf..7dfe8e990 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMappingRepository.java
@@ -18,11 +18,12 @@
  */
 package org.apache.fineract.portfolio.loanaccount.domain;
 
-import java.util.Optional;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
-public interface LoanTransactionRepository extends JpaRepository<LoanTransaction, Long>, JpaSpecificationExecutor<LoanTransaction> {
+public interface LoanTransactionToRepaymentScheduleMappingRepository extends JpaRepository<LoanTransactionToRepaymentScheduleMapping, Long>,
+        JpaSpecificationExecutor<LoanTransactionToRepaymentScheduleMapping> {
+
+    LoanTransactionToRepaymentScheduleMapping findByLoanTransaction(LoanTransaction loanTransaction);
 
-    Optional<LoanTransaction> findByIdAndLoanId(Long transactionId, Long loanId);
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index b13780e34..a52a241ca 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -540,8 +540,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
             for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
                 pastDueDate = currentInstallment.getDueDate();
                 if (!currentInstallment.isAdditional() && currentInstallment.getDueDate().isAfter(transactionDate)) {
-                    currentInstallment.addToPrincipal(transactionDate, transactionAmountUnprocessed);
                     currentInstallment.addToCredits(transactionAmountUnprocessed.getAmount());
+                    currentInstallment.addToPrincipal(transactionDate, transactionAmountUnprocessed);
                     transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, currentInstallment,
                             transactionAmountUnprocessed, zeroMoney, zeroMoney, zeroMoney));
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsConfig.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsConfig.java
index 3334b90ca..15cc21b5b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsConfig.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsConfig.java
@@ -21,7 +21,8 @@ package org.apache.fineract.portfolio.loanaccount.jobs.setloandelinquencytags;
 import lombok.AllArgsConstructor;
 import org.apache.fineract.infrastructure.jobs.service.JobName;
 import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
-import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.springframework.batch.core.Job;
 import org.springframework.batch.core.Step;
 import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
@@ -38,7 +39,8 @@ public class SetLoanDelinquencyTagsConfig {
     private StepBuilderFactory steps;
 
     private DelinquencyWritePlatformService delinquencyWritePlatformService;
-    private LoanReadPlatformService loanReadPlatformService;
+    private LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
+    private LoanTransactionRepository loanTransactionRepository;
 
     @Bean
     public Step setLoanDelinquencyTagsStep() {
@@ -53,7 +55,8 @@ public class SetLoanDelinquencyTagsConfig {
 
     @Bean
     public SetLoanDelinquencyTagsTasklet setLoanDelinquencyTagsTasklet() {
-        return new SetLoanDelinquencyTagsTasklet(delinquencyWritePlatformService, loanReadPlatformService);
+        return new SetLoanDelinquencyTagsTasklet(delinquencyWritePlatformService, loanRepaymentScheduleInstallmentRepository,
+                loanTransactionRepository);
     }
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsTasklet.java
index 9bf2d274b..5cfabe3f9 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsTasklet.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/setloandelinquencytags/SetLoanDelinquencyTagsTasklet.java
@@ -18,14 +18,20 @@
  */
 package org.apache.fineract.portfolio.loanaccount.jobs.setloandelinquencytags;
 
+import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformService;
 import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
-import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.springframework.batch.core.StepContribution;
 import org.springframework.batch.core.scope.context.ChunkContext;
 import org.springframework.batch.core.step.tasklet.Tasklet;
@@ -36,24 +42,54 @@ import org.springframework.batch.repeat.RepeatStatus;
 public class SetLoanDelinquencyTagsTasklet implements Tasklet {
 
     private final DelinquencyWritePlatformService delinquencyWritePlatformService;
-    private final LoanReadPlatformService loanReadPlatformService;
+    private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
+    private final LoanTransactionRepository loanTransactionRepository;
 
     @Override
     public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
-        log.debug("Run job for date {}", DateUtils.getBusinessLocalDate());
-        Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData = this.loanReadPlatformService
-                .retrieveScheduleDelinquencyData(DateUtils.getBusinessLocalDate());
-        log.debug("Were found {} items", loanScheduleDelinquencyData.size());
-        for (LoanScheduleDelinquencyData loanDelinquencyData : loanScheduleDelinquencyData) {
-            log.debug("Processing Loan {} with due date {} and {} overdue days", loanDelinquencyData.getLoanId(),
-                    loanDelinquencyData.getDueDate(), loanDelinquencyData.getAgeDays());
-            this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData.getLoanId(),
-                    loanDelinquencyData.getAgeDays());
-        }
 
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        log.debug("Run job for date {}", businessDate);
+
+        // Read Loan Ids with Loan Transaction Charge back
+        Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData = this.loanTransactionRepository
+                .fetchLoanTransactionsByTypeAndLessOrEqualDate(LoanTransactionType.CHARGEBACK.getValue(), businessDate);
+        List<Long> processedLoans = applyDelinquencyTagToLoans(loanScheduleDelinquencyData);
         log.debug("{}: Records affected by setLoanDelinquencyTags: {}", ThreadLocalContextUtil.getTenant().getName(),
-                loanScheduleDelinquencyData.size());
+                processedLoans.size());
+
+        // Read Loan Ids with overdue installments
+        if (processedLoans.isEmpty()) {
+            loanScheduleDelinquencyData = this.loanRepaymentScheduleInstallmentRepository
+                    .fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE.getValue(), businessDate, false);
+        } else {
+            loanScheduleDelinquencyData = this.loanRepaymentScheduleInstallmentRepository
+                    .fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE.getValue(), businessDate, false, processedLoans);
+        }
+        processedLoans = applyDelinquencyTagToLoans(loanScheduleDelinquencyData);
+
         return RepeatStatus.FINISHED;
     }
 
+    private List<Long> applyDelinquencyTagToLoans(Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData) {
+        List<Long> processedLoans = new ArrayList<>();
+
+        log.debug("Were found {} items", loanScheduleDelinquencyData.size());
+        for (LoanScheduleDelinquencyData loanDelinquencyData : loanScheduleDelinquencyData) {
+            // Set the data used by Delinquency Classification method
+            loanDelinquencyData = this.delinquencyWritePlatformService.calculateDelinquencyData(loanDelinquencyData);
+            log.debug("Processing Loan {} with {} overdue days since date {}", loanDelinquencyData.getLoanId(),
+                    loanDelinquencyData.getOverdueDays(), loanDelinquencyData.getOverdueSinceDate());
+            // Set or Unset the Delinquency Classification Tag
+            if (loanDelinquencyData.getOverdueDays() > 0) {
+                this.delinquencyWritePlatformService.applyDelinquencyTagToLoan(loanDelinquencyData);
+            } else {
+                this.delinquencyWritePlatformService.removeDelinquencyTagToLoan(loanDelinquencyData.getLoan());
+            }
+
+            processedLoans.add(loanDelinquencyData.getLoanId());
+        }
+        return processedLoans;
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index 11dae4619..1d1cc4ac3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -2671,7 +2671,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
 
         LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, loanApplicationTerms, loan, holidayDetailDTO,
                 loanRepaymentScheduleTransactionProcessor, onDate, calculateTill);
-        List<LoanTransaction> loanTransactions = loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals();
+        List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsPostDisbursementExcludeAccruals();
 
         loanRepaymentScheduleTransactionProcessor.handleTransaction(loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions,
                 currency, loanScheduleDTO.getInstallments(), loan.getActiveCharges());
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
index b47ff7547..cc2103f70 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
@@ -27,13 +27,11 @@ import org.apache.fineract.infrastructure.core.service.SearchParameters;
 import org.apache.fineract.organisation.staff.data.StaffData;
 import org.apache.fineract.portfolio.calendar.data.CalendarData;
 import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
-import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
-import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
@@ -109,8 +107,6 @@ public interface LoanReadPlatformService {
 
     Collection<LoanScheduleAccrualData> retriveScheduleAccrualData();
 
-    Collection<LoanScheduleDelinquencyData> retrieveScheduleDelinquencyData(LocalDate businessLocalDate);
-
     LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
 
     LoanTransactionData retrieveLoanWriteoffTemplate(Long loanId);
@@ -157,8 +153,6 @@ public interface LoanReadPlatformService {
 
     List<LoanRepaymentScheduleInstallmentData> getRepaymentDataResponse(Long loanId);
 
-    CollectionData retrieveLoanCollectionData(Long loanId);
-
     List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId);
 
 }
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 1dced1891..e90d0cedc 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
@@ -84,7 +84,6 @@ import org.apache.fineract.portfolio.group.data.GroupGeneralData;
 import org.apache.fineract.portfolio.group.data.GroupRoleData;
 import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
 import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
-import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData;
@@ -92,7 +91,6 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
-import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
@@ -240,7 +238,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
             this.context.authenticatedUser();
 
             final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
-                    repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled, totalPaidFeeCharges);
+                    repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled);
             final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
 
             return this.jdbcTemplate.query(sql, fullResultsetExtractor, loanId); // NOSONAR
@@ -639,7 +637,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                     + " l.loan_sub_status_id as loanSubStatusId, la.principal_overdue_derived as principalOverdue, l.is_fraud as isFraud, "
                     + " la.interest_overdue_derived as interestOverdue, la.fee_charges_overdue_derived as feeChargesOverdue,"
                     + " la.penalty_charges_overdue_derived as penaltyChargesOverdue, la.total_overdue_derived as totalOverdue,"
-                    + " la.overdue_since_date_derived as overdueSinceDate,"
+                    + " la.overdue_since_date_derived as overdueSinceDate, "
                     + " l.sync_disbursement_with_meeting as syncDisbursementWithMeeting,"
                     + " l.loan_counter as loanCounter, l.loan_product_counter as loanProductCounter,"
                     + " l.is_npa as isNPA, l.days_in_month_enum as daysInMonth, l.days_in_year_enum as daysInYear, "
@@ -678,7 +676,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                     + " left join m_code_value codev on codev.id = l.writeoff_reason_cv_id"
                     + " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id"
                     + " left join m_loan_topup as topup on l.id = topup.loan_id"
-                    + " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id";
+                    + " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id ";
 
         }
 
@@ -888,9 +886,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                 final BigDecimal totalRecovered = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalRecovered");
 
                 final LocalDate overdueSinceDate = JdbcSupport.getLocalDate(rs, "overdueSinceDate");
-                if (overdueSinceDate != null) {
-                    inArrears = true;
-                }
+                inArrears = (overdueSinceDate != null);
 
                 loanSummary = new LoanSummaryData(currencyData, principalDisbursed, principalAdjustments, principalPaid,
                         principalWrittenOff, principalOutstanding, principalOverdue, interestCharged, interestPaid, interestWaived,
@@ -1062,10 +1058,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         private LocalDate lastDueDate;
         private BigDecimal outstandingLoanPrincipalBalance;
         private boolean excludePastUndisbursed;
-        private final BigDecimal totalPaidFeeCharges;
 
         LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
-                Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, BigDecimal totalPaidFeeCharges) {
+                Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled) {
             this.currency = repaymentScheduleRelatedLoanData.getCurrency();
             this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
             this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
@@ -1073,7 +1068,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
             this.outstandingLoanPrincipalBalance = this.disbursement.getPrincipal();
             this.disbursementData = disbursementData;
             this.excludePastUndisbursed = isInterestRecalculationEnabled;
-            this.totalPaidFeeCharges = totalPaidFeeCharges;
         }
 
         public String schema() {
@@ -1699,25 +1693,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         }
     }
 
-    @Override
-    public Collection<LoanScheduleDelinquencyData> retrieveScheduleDelinquencyData(LocalDate businessLocalDate) {
-        LoanScheduleDelinquencyMapper mapper = new LoanScheduleDelinquencyMapper(DateUtils.getBusinessLocalDate());
-        final StringBuilder sqlBuilder = new StringBuilder(400);
-        sqlBuilder.append("select ").append(mapper.schema())
-                // Just get the overdued installments
-                .append(" where loan.loan_status_id=:active and ls.duedate <= :businessLocalDate and ")
-                // Just get the unpaid installments using the completed_derived flag
-                .append(" ls.completed_derived = false and ")
-                // Just the Loan Product linked to delinquency bucket
-                .append(" mpl.delinquency_bucket_id is not null ");
-        sqlBuilder.append(" group by ls.loan_id, loan.product_id ");
-        Map<String, Object> paramMap = new HashMap<>(3);
-        paramMap.put("active", LoanStatus.ACTIVE.getValue());
-        paramMap.put("businessLocalDate", businessLocalDate);
-
-        return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
-    }
-
     @Override
     public Collection<LoanScheduleAccrualData> retriveScheduleAccrualData() {
 
@@ -1887,33 +1862,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         }
     }
 
-    private static final class LoanScheduleDelinquencyMapper implements RowMapper<LoanScheduleDelinquencyData> {
-
-        private LocalDate businessDate;
-
-        LoanScheduleDelinquencyMapper(LocalDate businessDate) {
-            this.businessDate = businessDate;
-        }
-
-        public String schema() {
-            final StringBuilder sqlBuilder = new StringBuilder(400);
-            sqlBuilder.append(" ls.loan_id as loanId, loan.product_id as productId, min(ls.duedate) as duedate ")
-                    .append(" from m_loan_repayment_schedule ls left join m_loan loan on loan.id=ls.loan_id ")
-                    .append(" left join m_product_loan mpl on mpl.id = loan.product_id ");
-            return sqlBuilder.toString();
-        }
-
-        @Override
-        public LoanScheduleDelinquencyData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
-
-            final Long loanId = rs.getLong("loanId");
-            final Long productId = rs.getLong("productId");
-            final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "duedate");
-            final long ageDays = DateUtils.getDifferenceInDays(dueDate, businessDate);
-            return new LoanScheduleDelinquencyData(loanId, productId, dueDate, ageDays);
-        }
-    }
-
     @Override
     public LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId) {
         final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
@@ -2283,7 +2231,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
     @Override
     public Collection<Long> retrieveLoanIdsWithPendingIncomePostingTransactions() {
         LocalDate currentdate = DateUtils.getBusinessLocalDate();
-        StringBuilder sqlBuilder = new StringBuilder().append(" select distinct loan.id ").append(" from m_loan as loan ").append(
+        StringBuilder sqlBuilder = new StringBuilder().append(" select distinct loan.id from m_loan as loan ").append(
                 " inner join m_loan_recalculation_details as recdet on (recdet.loan_id = loan.id and recdet.is_compounding_to_be_posted_as_transaction is not null and recdet.is_compounding_to_be_posted_as_transaction = true) ")
                 .append(" inner join m_loan_repayment_schedule as repsch on repsch.loan_id = loan.id ")
                 .append(" inner join m_loan_interest_recalculation_additional_details as adddet on adddet.loan_repayment_schedule_id = repsch.id ")
@@ -2292,7 +2240,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                 .append(" and adddet.effective_date is not null ").append(" and trans.transaction_date is null ")
                 .append(" and adddet.effective_date < ? ");
         try {
-            return this.jdbcTemplate.queryForList(sqlBuilder.toString(), new Object[] { currentdate }, Long.class);
+            return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class, new Object[] { currentdate });
         } catch (final EmptyResultDataAccessException e) {
             return null;
         }
@@ -2438,62 +2386,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         return this.jdbcTemplate.queryForObject(sql, Integer.class);
     }
 
-    @Override
-    public CollectionData retrieveLoanCollectionData(Long loanId) {
-        final CollectionDataMapper mapper = new CollectionDataMapper(sqlGenerator);
-        String sql = "select " + mapper.schema();
-        CollectionData collectionData = this.jdbcTemplate.queryForObject(sql, mapper, loanId, loanId); // NOSONAR
-        return collectionData;
-    }
-
-    private static final class CollectionDataMapper implements RowMapper<CollectionData> {
-
-        private final DatabaseSpecificSQLGenerator sqlGenerator;
-
-        CollectionDataMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
-            this.sqlGenerator = sqlGenerator;
-        }
-
-        public String schema() {
-            StringBuilder sqlBuilder = new StringBuilder();
-
-            sqlBuilder.append(
-                    "l.id as loanId, coalesce((l.approved_principal - l.principal_disbursed_derived), 0) as availableDisbursementAmount, ");
-            sqlBuilder.append(
-                    sqlGenerator.dateDiff(sqlGenerator.currentBusinessDate(), "laa.overdue_since_date_derived") + " as pastDueDays, ");
-            sqlBuilder.append(
-                    "(select coalesce(min(lrs.duedate), null) as duedate from m_loan_repayment_schedule lrs where lrs.loan_id=l.id and lrs.completed_derived is false and lrs.duedate >= "
-                            + sqlGenerator.currentBusinessDate() + ") as nextPaymentDueDate, ");
-            sqlBuilder.append(
-                    sqlGenerator.dateDiff(sqlGenerator.currentBusinessDate(), "laa.overdue_since_date_derived") + " as delinquentDays, ");
-            sqlBuilder.append(sqlGenerator.currentBusinessDate()
-                    + " as delinquentDate, coalesce(laa.total_overdue_derived, 0) as delinquentAmount, ");
-            sqlBuilder.append("lre.transactionDate as lastPaymentDate, coalesce(lre.amount, 0) as lastPaymentAmount ");
-            sqlBuilder.append("from m_loan l left join m_loan_arrears_aging laa on laa.loan_id = l.id ");
-            sqlBuilder.append(
-                    "left join (select lt.loan_id, lt.transaction_date as transactionDate, lt.amount as amount from m_loan_transaction lt ");
-            sqlBuilder.append(
-                    "where lt.loan_id =? and lt.is_reversed = false and lt.transaction_type_enum=2 order by lt.transaction_date desc, lt.id desc limit 1) lre on lre.loan_id = l.id ");
-            sqlBuilder.append("where l.id=? ");
-            return sqlBuilder.toString();
-        }
-
-        @Override
-        public CollectionData mapRow(ResultSet rs, int rowNum) throws SQLException {
-            final LocalDate nextPaymentDueDate = JdbcSupport.getLocalDate(rs, "nextPaymentDueDate");
-            final LocalDate delinquentDate = JdbcSupport.getLocalDate(rs, "delinquentDate");
-            final LocalDate lastPaymentDate = JdbcSupport.getLocalDate(rs, "lastPaymentDate");
-            final BigDecimal availableDisbursementAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "availableDisbursementAmount");
-            final BigDecimal delinquentAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "delinquentAmount");
-            final BigDecimal lastPaymentAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "lastPaymentAmount");
-            final int pastDueDays = rs.getInt("pastDueDays");
-            final int delinquentDays = rs.getInt("delinquentDays");
-
-            return CollectionData.instance(availableDisbursementAmount, pastDueDays, nextPaymentDueDate, delinquentDays, delinquentDate,
-                    delinquentAmount, lastPaymentDate, lastPaymentAmount);
-        }
-    }
-
     @Override
     public List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId) {
         final LoanTransaction loanTransaction = this.loanTransactionRepository.getReferenceById(loanTransactionId);
@@ -2501,4 +2393,5 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                 .findByFromTransactionAndRelationType(loanTransaction, LoanTransactionRelationTypeEnum.CHARGEBACK);
         return loanTransactionRelationMapper.map(loanTransactionRelations);
     }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 1db95735b..608353ff2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -1289,7 +1289,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         }
 
         postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-        this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+        this.loanAccountDomainService.setLoanDelinquencyTag(loan, transactionDate);
 
         businessEventNotifierService.notifyPostBusinessEvent(new LoanChargebackTransactionBusinessEvent(loanTransaction));
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java
index 60eec4dc7..c78abf37f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java
@@ -388,7 +388,7 @@ public class FixedDepositAccount extends SavingsAccount {
                 throw new PlatformApiDataValidationException(dataValidationErrors);
             }
         }
-        final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
         if (savingsAccountTransactions.size() > 0) {
             final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
             if (accountTransaction.isAfter(closedDate)) {
@@ -473,7 +473,7 @@ public class FixedDepositAccount extends SavingsAccount {
             }
         }
 
-        final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
         if (savingsAccountTransactions.size() > 0) {
             final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
             if (accountTransaction.isAfter(closedDate)) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java
index 7d59e1825..a602e55ff 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java
@@ -492,7 +492,7 @@ public class RecurringDepositAccount extends SavingsAccount {
             }
         }
 
-        final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
         if (savingsAccountTransactions.size() > 0) {
             final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
             if (accountTransaction.isAfter(closedDate)) {
@@ -593,7 +593,7 @@ public class RecurringDepositAccount extends SavingsAccount {
                 throw new PlatformApiDataValidationException(dataValidationErrors);
             }
         }
-        final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
         if (savingsAccountTransactions.size() > 0) {
             final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
             if (accountTransaction.isAfter(closedDate)) {
@@ -934,7 +934,7 @@ public class RecurringDepositAccount extends SavingsAccount {
     }
 
     private List<SavingsAccountTransaction> retreiveOrderedDepositTransactions() {
-        final List<SavingsAccountTransaction> listOfTransactionsSorted = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> listOfTransactionsSorted = retrieveListOfTransactions();
 
         final List<SavingsAccountTransaction> orderedDepositTransactions = new ArrayList<>();
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
index ae10f099a..a65f9b602 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
@@ -928,7 +928,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
     }
 
     protected List<SavingsAccountTransaction> retreiveOrderedNonInterestPostingTransactions() {
-        final List<SavingsAccountTransaction> listOfTransactionsSorted = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> listOfTransactionsSorted = retrieveListOfTransactions();
 
         final List<SavingsAccountTransaction> orderedNonInterestPostingTransactions = new ArrayList<>();
 
@@ -965,7 +965,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
         return listOfTransactionsSorted;
     }
 
-    protected List<SavingsAccountTransaction> retreiveListOfTransactions() {
+    protected List<SavingsAccountTransaction> retrieveListOfTransactions() {
         final List<SavingsAccountTransaction> listOfTransactionsSorted = new ArrayList<>();
         listOfTransactionsSorted.addAll(this.transactions);
 
@@ -983,7 +983,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
         if (backdatedTxnsAllowedTill) {
             accountTransactionsSorted = retrieveSortedTransactions();
         } else {
-            accountTransactionsSorted = retreiveListOfTransactions();
+            accountTransactionsSorted = retrieveListOfTransactions();
         }
 
         boolean isTransactionsModified = false;
@@ -1058,7 +1058,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
             if (backdatedTxnsAllowedTill) {
                 accountTransactionsSorted = retrieveSortedTransactions();
             } else {
-                accountTransactionsSorted = retreiveListOfTransactions();
+                accountTransactionsSorted = retrieveListOfTransactions();
             }
         }
         resetAccountTransactionsEndOfDayBalances(accountTransactionsSorted, interestPostingUpToDate);
@@ -1415,7 +1415,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
         boolean transactionBeforeLastInterestPosting = false;
 
         if (!backdatedTxnsAllowedTill) {
-            for (final SavingsAccountTransaction transaction : retreiveListOfTransactions()) {
+            for (final SavingsAccountTransaction transaction : retrieveListOfTransactions()) {
                 if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
                         && transaction.isAfter(transactionDate) && !transaction.isReversalTransaction()) {
                     transactionBeforeLastInterestPosting = true;
@@ -1441,7 +1441,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
         if (backdatedTxnsAllowedTill) {
             transactionsSortedByDate = retrieveSortedTransactions();
         } else {
-            transactionsSortedByDate = retreiveListOfTransactions();
+            transactionsSortedByDate = retrieveListOfTransactions();
         }
 
         Money runningBalance = Money.zero(this.currency);
@@ -1524,7 +1524,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
         if (backdatedTxnsAllowedTill) {
             transactionsSortedByDate = retrieveSortedTransactions();
         } else {
-            transactionsSortedByDate = retreiveListOfTransactions();
+            transactionsSortedByDate = retrieveListOfTransactions();
         }
         Money runningBalance = Money.zero(this.currency);
 
@@ -2507,7 +2507,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
     }
 
     public void validateAccountBalanceDoesNotBecomeNegativeMinimal(final BigDecimal transactionAmount, final boolean isException) {
-        // final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
+        // final List<SavingsAccountTransaction> transactionsSortedByDate = retrieveListOfTransactions();
         Money runningBalance = this.summary.getAccountBalance(getCurrency());
         Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
         org.joda.time.LocalDate lastSavingsDate = null;
@@ -2846,7 +2846,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
                 throw new PlatformApiDataValidationException(dataValidationErrors);
             }
         }
-        final List<SavingsAccountTransaction> savingsAccountTransactions = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> savingsAccountTransactions = retrieveListOfTransactions();
         if (savingsAccountTransactions.size() > 0) {
             final SavingsAccountTransaction accountTransaction = savingsAccountTransactions.get(savingsAccountTransactions.size() - 1);
             if (accountTransaction.isAfter(closedDate)) {
@@ -3806,7 +3806,7 @@ public class SavingsAccount extends AbstractPersistableCustom {
     }
 
     public LocalDate retrieveLastTransactionDate() {
-        final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
+        final List<SavingsAccountTransaction> transactionsSortedByDate = retrieveListOfTransactions();
         SavingsAccountTransaction lastTransaction = null;
         if (transactionsSortedByDate.size() > 0) {
             lastTransaction = transactionsSortedByDate.get(transactionsSortedByDate.size() - 1);
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 344dd2bc0..cbfd124fa 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
@@ -305,7 +305,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
     }
 
     private List<SavingsAccountTransactionData> retreiveOrderedNonInterestPostingTransactions(final SavingsAccountData savingsAccountData) {
-        final List<SavingsAccountTransactionData> listOfTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
+        final List<SavingsAccountTransactionData> listOfTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
 
         final List<SavingsAccountTransactionData> orderedNonInterestPostingTransactions = new ArrayList<>();
 
@@ -319,7 +319,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
         return orderedNonInterestPostingTransactions;
     }
 
-    private List<SavingsAccountTransactionData> retreiveListOfTransactions(final SavingsAccountData savingsAccountData) {
+    private List<SavingsAccountTransactionData> retrieveListOfTransactions(final SavingsAccountData savingsAccountData) {
         final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>();
         listOfTransactionsSorted.addAll(savingsAccountData.getSavingsAccountTransactionData());
 
@@ -407,7 +407,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
 
         Money runningBalance = openingAccountBalance.copy();
 
-        List<SavingsAccountTransactionData> accountTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
+        List<SavingsAccountTransactionData> accountTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
         boolean isTransactionsModified = false;
 
         for (final SavingsAccountTransactionData transaction : accountTransactionsSorted) {
@@ -466,7 +466,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
         }
 
         if (isTransactionsModified) {
-            accountTransactionsSorted = retreiveListOfTransactions(savingsAccountData);
+            accountTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
         }
         resetAccountTransactionsEndOfDayBalances(accountTransactionsSorted, interestPostingUpToDate, savingsAccountData);
     }
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index f0f92f568..e2750a085 100644
--- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -85,4 +85,5 @@
     <include file="parts/0063_add_permissions_for_external_event_configuration.xml" relativeToChangelogFile="true"/>
     <include file="parts/0064_refactor_loan_transaction_strategy.xml" relativeToChangelogFile="true"/>
     <include file="parts/0065_add_bypass_loan_write_transaction_permission.xml" relativeToChangelogFile="true"/>
+    <include file="parts/0066_delinquency_classification_chargeback.xml" relativeToChangelogFile="true"/>
 </databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0066_delinquency_classification_chargeback.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0066_delinquency_classification_chargeback.xml
new file mode 100644
index 000000000..d7b17251e
--- /dev/null
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0066_delinquency_classification_chargeback.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet id="1" author="fineract">
+        <createIndex indexName="ind_m_loan_delinquency_tag_history_loan_id" tableName="m_loan_delinquency_tag_history">
+            <column name="loan_id"/>
+        </createIndex>
+        <createIndex indexName="ind_m_loan_delinquency_tag_history_liftedon_date" tableName="m_loan_delinquency_tag_history">
+            <column name="liftedon_date"/>
+        </createIndex>
+    </changeSet>
+</databaseChangeLog>
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyAndChargebackIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyAndChargebackIntegrationTest.java
new file mode 100644
index 000000000..ee25a5c40
--- /dev/null
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyAndChargebackIntegrationTest.java
@@ -0,0 +1,192 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+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.time.LocalDate;
+import java.util.HashMap;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+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.loans.LoanApplicationTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class DelinquencyAndChargebackIntegrationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private LoanTransactionHelper loanTransactionHelper;
+    private static final String principalAmount = "1200.00";
+    private static final Double doubleZERO = Double.valueOf("0.00");
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+
+        loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
+    }
+
+    @Test
+    public void testLoanClassificationStepAsPartOfCOB() {
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
+
+        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
+        LocalDate businessDate = todaysDate.minusDays(57);
+        log.info("Current Business date {}", businessDate);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
+
+        final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
+        // Delinquency Bucket
+        final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketId);
+
+        // Client and Loan account creation
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
+                delinquencyBucket.getId());
+        assertNotNull(getLoanProductsProductResponse);
+
+        log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
+        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
+
+        // Older date to have more than one overdue installment
+        LocalDate transactionDate = businessDate;
+        String operationDate = Utils.dateFormatter.format(transactionDate);
+        log.info("Operation date {}", transactionDate);
+
+        // Create Loan Account
+        final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
+                getLoanProductsProductResponse.getId().toString(), operationDate);
+
+        // Run first time the Loan COB Job
+        final String jobName = "Loan COB";
+        schedulerJobHelper.executeAndAwaitJob(jobName);
+
+        // Get loan details expecting to have not a delinquency classification
+        GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        validateLoanAccount(getLoansLoanIdResponse, "0.00", principalAmount, 0, doubleZERO);
+
+        // Move the Business date to get older the loan and to have an overdue loan
+        businessDate = businessDate.plusDays(43);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
+        log.info("Current Business date {}", businessDate);
+
+        String amountVal = "100.00";
+        Float transactionAmount = Float.valueOf(amountVal);
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate,
+                transactionAmount, loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = loanIdTransactionsResponse.getResourceId();
+        loanTransactionHelper.reviewLoanTransactionRelations(loanId, transactionId, 0);
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        validateLoanAccount(getLoansLoanIdResponse, "0.00", "1100.00", 0, doubleZERO);
+
+        final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, amountVal, 0,
+                responseSpec);
+        loanTransactionHelper.reviewLoanTransactionRelations(loanId, transactionId, 1);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+        // Past Due Days in Zero because the Charge back transaction exists and It was done with the current date
+        validateLoanAccount(getLoansLoanIdResponse, amountVal, principalAmount, 0, Double.valueOf("100.00"));
+
+        // Move the Business date to get older the loan and to have an overdue loan
+        businessDate = businessDate.plusDays(9);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
+        log.info("Current Business date {}", businessDate);
+        // Run Second time the Job
+        schedulerJobHelper.executeAndAwaitJob(jobName);
+
+        // Get loan details expecting to have a delinquency classification
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+
+        // The value is lower due the 3 grace days
+        validateLoanAccount(getLoansLoanIdResponse, "100.00", principalAmount, 9, Double.valueOf("100.00"));
+
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
+    }
+
+    private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
+            final Integer delinquencyBucketId) {
+        final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId);
+        final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
+        return loanTransactionHelper.getLoanProduct(loanProductId);
+    }
+
+    private Integer createLoanAccount(final LoanTransactionHelper loanTransactionHelper, final String clientId, final String loanProductId,
+            final String operationDate) {
+        final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principalAmount).withLoanTermFrequency("12")
+                .withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
+                .withRepaymentFrequencyTypeAsMonths() //
+                .withInterestRatePerPeriod("0") //
+                .withExpectedDisbursementDate(operationDate) //
+                .withInterestTypeAsDecliningBalance() //
+                .withSubmittedOnDate(operationDate) //
+                .build(clientId, loanProductId, null);
+        final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON);
+        loanTransactionHelper.approveLoan(operationDate, principalAmount, loanId, null);
+        loanTransactionHelper.disburseLoanWithNetDisbursalAmount(operationDate, loanId, principalAmount);
+        return loanId;
+    }
+
+    private GetDelinquencyRangesResponse validateLoanAccount(GetLoansLoanIdResponse getLoansLoanIdResponse, final String adjustments,
+            final String outstanding, Integer pastDueDays, Double delinquentAmount) {
+        assertNotNull(getLoansLoanIdResponse);
+        final GetDelinquencyRangesResponse delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
+
+        log.info("Loan Delinquency Range is null {}", (delinquencyRange == null));
+        if (delinquencyRange != null) {
+            log.info("Loan Delinquency Range is {}", delinquencyRange.getClassification());
+        }
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        loanTransactionHelper.evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, Double.valueOf(adjustments));
+        DelinquencyBucketsHelper.evaluateLoanCollectionData(getLoansLoanIdResponse, pastDueDays, delinquentAmount);
+
+        loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf(outstanding));
+
+        return delinquencyRange;
+    }
+
+}
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 282ee48f1..fbc2e73ed 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
@@ -234,196 +234,6 @@ public class DelinquencyBucketsIntegrationTest {
         DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, response403Spec, jsonBucket);
     }
 
-    @Test
-    public void testLoanClassificationJob() {
-        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
-
-        LocalDate businessDate = Utils.getLocalDateOfTenant();
-        businessDate = businessDate.minusDays(57);
-        log.info("Current date {}", businessDate);
-        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
-
-        final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
-
-        ArrayList<Integer> rangeIds = new ArrayList<>();
-        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
-        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
-                jsonRange);
-        rangeIds.add(delinquencyRangeResponse.getResourceId());
-        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
-        // Create
-        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
-        rangeIds.add(delinquencyRangeResponse.getResourceId());
-
-        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
-                delinquencyRangeResponse.getResourceId());
-        final String classificationExpected = range.getClassification();
-        log.info("Expected Delinquency Range classification {}", classificationExpected);
-
-        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
-        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
-                responseSpec, jsonBucket);
-        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
-                delinquencyBucketResponse.getResourceId());
-
-        // Client and Loan account creation
-        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
-                delinquencyBucket.getId());
-        assertNotNull(getLoanProductsProductResponse);
-        log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
-        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
-
-        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
-        // Older date to have more than one overdue installment
-        final LocalDate transactionDate = todaysDate.minusDays(65);
-        String operationDate = Utils.dateFormatter.format(transactionDate);
-
-        // Create Loan Account
-        final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                getLoanProductsProductResponse.getId().toString(), operationDate);
-
-        // Run first time the Job
-        final String jobName = "Loan Delinquency Classification";
-        schedulerJobHelper.executeAndAwaitJob(jobName);
-
-        // Get loan details expecting to have not a delinquency classification
-        GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
-        log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
-        GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = getLoansLoanIdResponse.getRepaymentSchedule();
-        if (getLoanRepaymentSchedule != null) {
-            log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
-            for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
-                log.info("Period number {} for due date {} and outstanding {}", period.getPeriod(), period.getDueDate(),
-                        period.getTotalOutstandingForPeriod());
-            }
-        }
-
-        // Move the Business date to get older the loan and to have an overdue loan
-        businessDate = businessDate.plusDays(40);
-        log.info("Current date {}", businessDate);
-        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
-        // Run Second time the Job
-        schedulerJobHelper.executeAndAwaitJob(jobName);
-
-        // Get loan details expecting to have a delinquency classification
-        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        final GetDelinquencyRangesResponse secondTestCase = getLoansLoanIdResponse.getDelinquencyRange();
-        log.info("Loan Delinquency Range is {}", secondTestCase.getClassification());
-
-        // Then
-        assertNotNull(delinquencyBucketResponse);
-        assertNotNull(getLoanProductsProductResponse);
-        assertNull(firstTestCase);
-        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
-        assertNotNull(secondTestCase);
-        assertEquals(secondTestCase.getClassification(), classificationExpected);
-
-        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
-    }
-
-    @Test
-    public void testLoanClassificationStepAsPartOfCOB() {
-        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
-
-        LocalDate businessDate = Utils.getLocalDateOfTenant();
-        businessDate = businessDate.minusDays(57);
-        log.info("Current date {}", businessDate);
-        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
-
-        // Given
-        final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
-
-        ArrayList<Integer> rangeIds = new ArrayList<>();
-        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
-        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
-                jsonRange);
-        rangeIds.add(delinquencyRangeResponse.getResourceId());
-        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
-        // Create
-        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
-        rangeIds.add(delinquencyRangeResponse.getResourceId());
-
-        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
-                delinquencyRangeResponse.getResourceId());
-        final String classificationExpected = range.getClassification();
-        log.info("Expected Delinquency Range classification {}", classificationExpected);
-
-        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
-        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
-                responseSpec, jsonBucket);
-        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
-                delinquencyBucketResponse.getResourceId());
-
-        // Client and Loan account creation
-        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
-                delinquencyBucket.getId());
-        assertNotNull(getLoanProductsProductResponse);
-        log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
-        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
-
-        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
-        // Older date to have more than one overdue installment
-        final LocalDate transactionDate = todaysDate.minusDays(65);
-        String operationDate = Utils.dateFormatter.format(transactionDate);
-
-        // Create Loan Account
-        final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                getLoanProductsProductResponse.getId().toString(), operationDate);
-
-        // COB Step Validation
-        final JobBusinessStepConfigData jobBusinessStepConfigData = BusinessStepConfigurationHelper
-                .getConfiguredBusinessStepsByJobName(requestSpec, responseSpec, BusinessConfigurationApiTest.LOAN_JOB_NAME);
-        assertNotNull(jobBusinessStepConfigData);
-        assertEquals(BusinessConfigurationApiTest.LOAN_JOB_NAME, jobBusinessStepConfigData.getJobName());
-        assertTrue(jobBusinessStepConfigData.getBusinessSteps().size() > 0);
-        assertTrue(jobBusinessStepConfigData.getBusinessSteps().stream()
-                .anyMatch(businessStep -> BusinessConfigurationApiTest.LOAN_DELINQUENCY_CLASSIFICATION.equals(businessStep.getStepName())));
-
-        // Run first time the Loan COB Job
-        final String jobName = "Loan COB";
-        schedulerJobHelper.executeAndAwaitJob(jobName);
-
-        // Get loan details expecting to have not a delinquency classification
-        GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
-        log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
-        GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = getLoansLoanIdResponse.getRepaymentSchedule();
-        if (getLoanRepaymentSchedule != null) {
-            log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
-            for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
-                log.info("Period number {} for due date {} and outstanding {}", period.getPeriod(), period.getDueDate(),
-                        period.getTotalOutstandingForPeriod());
-            }
-        }
-
-        // Move the Business date to get older the loan and to have an overdue loan
-        businessDate = businessDate.plusDays(40);
-        log.info("Current date {}", businessDate);
-        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
-        // Run Second time the Job
-        schedulerJobHelper.executeAndAwaitJob(jobName);
-
-        // Get loan details expecting to have a delinquency classification
-        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        final GetDelinquencyRangesResponse secondTestCase = getLoansLoanIdResponse.getDelinquencyRange();
-        log.info("Loan Delinquency Range is {}", secondTestCase.getClassification());
-
-        // Then
-        assertNotNull(delinquencyBucketResponse);
-        assertNotNull(getLoanProductsProductResponse);
-        assertNull(firstTestCase);
-        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
-        assertNotNull(secondTestCase);
-        assertEquals(secondTestCase.getClassification(), classificationExpected);
-
-        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
-    }
-
     @Test
     public void testLoanClassificationRealtime() {
         // Given
@@ -465,7 +275,7 @@ public class DelinquencyBucketsIntegrationTest {
 
         final LocalDate todaysDate = Utils.getLocalDateOfTenant();
         // Older date to have more than one overdue installment
-        final LocalDate transactionDate = todaysDate.minusDays(45);
+        final LocalDate transactionDate = todaysDate.minusDays(50);
         String operationDate = Utils.dateFormatter.format(transactionDate);
 
         // Create Loan Account
@@ -512,6 +322,8 @@ public class DelinquencyBucketsIntegrationTest {
 
     @Test
     public void testLoanClassificationRealtimeWithCharges() {
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
+
         // Given
         final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
 
@@ -551,7 +363,7 @@ public class DelinquencyBucketsIntegrationTest {
 
         final LocalDate todaysDate = Utils.getLocalDateOfTenant();
         // Older date to have more than one overdue installment
-        LocalDate transactionDate = todaysDate.minusDays(45);
+        LocalDate transactionDate = todaysDate.minusMonths(2).minusDays(5);
         String operationDate = Utils.dateFormatter.format(transactionDate);
 
         // Create Loan Account
@@ -563,18 +375,18 @@ public class DelinquencyBucketsIntegrationTest {
         assertNotNull(getLoansLoanIdResponse);
         // First Loan Delinquency Classification after Disbursement command
         assertEquals(getLoansLoanIdResponse.getDelinquencyRange().getClassification(), classificationExpected);
-        printRepaymentSchedule(getLoansLoanIdResponse);
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
 
         // Apply a repayment to get a full paid installment
         operationDate = Utils.dateFormatter.format(todaysDate);
-        loanTransactionHelper.makeLoanRepayment(operationDate, 1033.33f, loanId);
+        loanTransactionHelper.makeLoanRepayment(operationDate, 2049.99f, loanId);
 
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        log.info("Loan Delinquency Range after Repayment {}", getLoansLoanIdResponse.getDelinquencyRange());
         assertNotNull(getLoansLoanIdResponse);
         // The Loan Delinquency Classification after Repayment command must be null
+        log.info("Loan Delinquency Range after Repayment {}", getLoansLoanIdResponse.getDelinquencyRange());
         assertNull(getLoansLoanIdResponse.getDelinquencyRange());
-        printRepaymentSchedule(getLoansLoanIdResponse);
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
 
         transactionDate = todaysDate.minusDays(18);
         operationDate = Utils.dateFormatter.format(transactionDate);
@@ -588,7 +400,7 @@ public class DelinquencyBucketsIntegrationTest {
         assertNotNull(loanChargeId);
 
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        printRepaymentSchedule(getLoansLoanIdResponse);
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
 
         log.info("Loan Delinquency Range after add Loan Charge {}", getLoansLoanIdResponse.getDelinquencyRange());
         assertNotNull(getLoansLoanIdResponse.getDelinquencyRange());
@@ -598,6 +410,8 @@ public class DelinquencyBucketsIntegrationTest {
 
     @Test
     public void testLoanClassificationRealtimeOlderLoan() {
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
+
         // Given
         final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
 
@@ -645,7 +459,7 @@ public class DelinquencyBucketsIntegrationTest {
 
         final LocalDate todaysDate = Utils.getLocalDateOfTenant();
         // Older date to have more than one overdue installment
-        final LocalDate transactionDate = todaysDate.minusDays(85);
+        LocalDate transactionDate = todaysDate.minusDays(85);
         String operationDate = Utils.dateFormatter.format(transactionDate);
 
         // Create Loan Account
@@ -663,7 +477,8 @@ public class DelinquencyBucketsIntegrationTest {
         loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
 
         // Apply a repayment to get a first full paid installment
-        operationDate = Utils.dateFormatter.format(todaysDate);
+        transactionDate = todaysDate.minusDays(1);
+        operationDate = Utils.dateFormatter.format(transactionDate);
         PostLoansLoanIdTransactionsResponse loansLoanIdTransactions = loanTransactionHelper.makeLoanRepayment(operationDate, 1050.0f,
                 loanId);
         assertNotNull(loansLoanIdTransactions);
@@ -736,8 +551,10 @@ public class DelinquencyBucketsIntegrationTest {
         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(45);
+        final LocalDate transactionDate = todaysDate.minusDays(50);
         String operationDate = Utils.dateFormatter.format(transactionDate);
 
         // Create Loan Account
@@ -745,8 +562,11 @@ public class DelinquencyBucketsIntegrationTest {
                 getLoanProductsProductResponse.getId().toString(), operationDate);
 
         GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        assertNotNull(getLoansLoanIdResponse);
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
+
         log.info("Loan Delinquency Range after Disbursement in null? {}", (getLoansLoanIdResponse.getDelinquencyRange() == null));
+        assertNotNull(getLoansLoanIdResponse);
         assertNotNull(getLoansLoanIdResponse.getDelinquencyRange());
         log.info("Loan Delinquency Range after Disbursement {}", getLoansLoanIdResponse.getDelinquencyRange());
         // First Loan Delinquency Classification after Disbursement command
@@ -796,6 +616,194 @@ public class DelinquencyBucketsIntegrationTest {
         log.info("Loan Id {} with final Loan status {}", getLoansLoanIdResponse.getId(), getLoansLoanIdResponse.getStatus().getCode());
     }
 
+    @Test
+    public void testLoanClassificationJob() {
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
+
+        LocalDate businessDate = Utils.getLocalDateOfTenant();
+        businessDate = businessDate.minusDays(37);
+        log.info("Current date {}", businessDate);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
+
+        final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
+
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
+        // Create
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse.getResourceId());
+        final String classificationExpected = range.getClassification();
+        log.info("Expected Delinquency Range classification {}", classificationExpected);
+
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // Client and Loan account creation
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
+                delinquencyBucket.getId());
+        assertNotNull(getLoanProductsProductResponse);
+        log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
+        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
+
+        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
+        // Older date to have more than one overdue installment
+        final LocalDate transactionDate = todaysDate.minusDays(57);
+        String operationDate = Utils.dateFormatter.format(transactionDate);
+
+        // Create Loan Account
+        final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
+                getLoanProductsProductResponse.getId().toString(), operationDate);
+
+        // Run first time the Job
+        final String jobName = "Loan Delinquency Classification";
+        schedulerJobHelper.executeAndAwaitJob(jobName);
+
+        // Get loan details expecting to have not a delinquency classification
+        GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
+        final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
+        log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        // Move the Business date to get older the loan and to have an overdue loan
+        businessDate = businessDate.plusMonths(1);
+        log.info("Current date {}", businessDate);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
+        // Run Second time the Job
+        schedulerJobHelper.executeAndAwaitJob(jobName);
+
+        // Get loan details expecting to have a delinquency classification
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
+
+        final GetDelinquencyRangesResponse secondTestCase = getLoansLoanIdResponse.getDelinquencyRange();
+        assertNotNull(secondTestCase);
+        log.info("Loan Delinquency Range is {}", secondTestCase.getClassification());
+
+        // Then
+        assertNotNull(delinquencyBucketResponse);
+        assertNotNull(getLoanProductsProductResponse);
+        assertNull(firstTestCase);
+        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
+        assertEquals(secondTestCase.getClassification(), classificationExpected);
+
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
+    }
+
+    @Test
+    public void testLoanClassificationStepAsPartOfCOB() {
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
+
+        LocalDate businessDate = Utils.getLocalDateOfTenant();
+        businessDate = businessDate.minusDays(57);
+        log.info("Current date {}", businessDate);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
+
+        // Given
+        final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
+
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
+        // Create
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+
+        final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse.getResourceId());
+        final String classificationExpected = range.getClassification();
+        log.info("Expected Delinquency Range classification {}", classificationExpected);
+
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketResponse.getResourceId());
+
+        // Client and Loan account creation
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
+                delinquencyBucket.getId());
+        assertNotNull(getLoanProductsProductResponse);
+        log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
+        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
+
+        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
+        // Older date to have more than one overdue installment
+        final LocalDate transactionDate = todaysDate.minusDays(65);
+        String operationDate = Utils.dateFormatter.format(transactionDate);
+
+        // Create Loan Account
+        final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
+                getLoanProductsProductResponse.getId().toString(), operationDate);
+
+        // COB Step Validation
+        final JobBusinessStepConfigData jobBusinessStepConfigData = BusinessStepConfigurationHelper
+                .getConfiguredBusinessStepsByJobName(requestSpec, responseSpec, BusinessConfigurationApiTest.LOAN_JOB_NAME);
+        assertNotNull(jobBusinessStepConfigData);
+        assertEquals(BusinessConfigurationApiTest.LOAN_JOB_NAME, jobBusinessStepConfigData.getJobName());
+        assertTrue(jobBusinessStepConfigData.getBusinessSteps().size() > 0);
+        assertTrue(jobBusinessStepConfigData.getBusinessSteps().stream()
+                .anyMatch(businessStep -> BusinessConfigurationApiTest.LOAN_DELINQUENCY_CLASSIFICATION.equals(businessStep.getStepName())));
+
+        // Run first time the Loan COB Job
+        final String jobName = "Loan COB";
+        schedulerJobHelper.executeAndAwaitJob(jobName);
+
+        // Get loan details expecting to have not a delinquency classification
+        GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
+        log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
+        GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = getLoansLoanIdResponse.getRepaymentSchedule();
+        if (getLoanRepaymentSchedule != null) {
+            log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
+            for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
+                log.info("Period number {} for due date {} and outstanding {}", period.getPeriod(), period.getDueDate(),
+                        period.getTotalOutstandingForPeriod());
+            }
+        }
+
+        // Move the Business date to get older the loan and to have an overdue loan
+        businessDate = businessDate.plusDays(50);
+        log.info("Current date {}", businessDate);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
+        // Run Second time the Job
+        schedulerJobHelper.executeAndAwaitJob(jobName);
+
+        // Get loan details expecting to have a delinquency classification
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+        final GetDelinquencyRangesResponse secondTestCase = getLoansLoanIdResponse.getDelinquencyRange();
+        assertNotNull(secondTestCase);
+        log.info("Loan Delinquency Range is {}", secondTestCase.getClassification());
+
+        // Then
+        assertNotNull(delinquencyBucketResponse);
+        assertNotNull(getLoanProductsProductResponse);
+        assertNull(firstTestCase);
+        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
+        assertEquals(secondTestCase.getClassification(), classificationExpected);
+
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
+    }
+
     private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
             final Integer delinquencyBucketId) {
         final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId);
@@ -819,17 +827,6 @@ public class DelinquencyBucketsIntegrationTest {
         return loanId;
     }
 
-    private void printRepaymentSchedule(GetLoansLoanIdResponse getLoansLoanIdResponse) {
-        GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = getLoansLoanIdResponse.getRepaymentSchedule();
-        if (getLoanRepaymentSchedule != null) {
-            log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
-            for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
-                log.info("Period number {} for due date {} and outstanding {}", period.getPeriod(), period.getDueDate(),
-                        period.getTotalOutstandingForPeriod());
-            }
-        }
-    }
-
     private String getChargeApplyJSON(final Integer chargeId, final String dueDate) {
         final HashMap<String, Object> map = new HashMap<>();
         map.put("chargeId", chargeId);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
index e7d5612bf..b4426d739 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
@@ -20,9 +20,8 @@ package org.apache.fineract.integrationtests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
-import com.google.gson.Gson;
 import io.restassured.builder.RequestSpecBuilder;
 import io.restassured.builder.ResponseSpecBuilder;
 import io.restassured.http.ContentType;
@@ -30,26 +29,23 @@ import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
 import java.time.LocalDate;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
-import org.apache.fineract.client.models.GetLoansLoanIdSummary;
 import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
 import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
-import org.apache.fineract.client.models.GetPaymentTypesResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
-import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.CommonConstants;
-import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -99,7 +95,8 @@ public class LoanTransactionChargebackTest {
 
         reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
 
-        final Integer chargebackTransactionId = applyChargebackTransaction(loanId, transactionId, "1000.00", 0, responseSpec);
+        final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "1000.00", 0,
+                responseSpec);
 
         reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("1000.00"));
@@ -141,7 +138,8 @@ public class LoanTransactionChargebackTest {
         final Integer transactionId = loanIdTransactionsResponse.getResourceId();
         reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("666.67"));
 
-        final Integer chargebackTransactionId = applyChargebackTransaction(loanId, transactionId, amount.toString(), 0, responseSpec);
+        final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, amount.toString(),
+                0, responseSpec);
         reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("666.67"));
         reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("1000.00"));
 
@@ -160,7 +158,8 @@ public class LoanTransactionChargebackTest {
             }
         }
 
-        evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, Double.valueOf(baseAmount));
+        loanTransactionHelper.evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, Double.valueOf(baseAmount));
+        DelinquencyBucketsHelper.evaluateLoanCollectionData(getLoansLoanIdResponse, 0, Double.valueOf("333.33"));
     }
 
     @Test
@@ -179,7 +178,7 @@ public class LoanTransactionChargebackTest {
         log.info("Try to apply the Charge back over transaction Id {} with type {}", loanTransaction.getId(),
                 loanTransaction.getType().getCode());
 
-        applyChargebackTransaction(loanId, loanTransaction.getId().intValue(), amountVal, 0, responseSpecError);
+        loanTransactionHelper.applyChargebackTransaction(loanId, loanTransaction.getId().intValue(), amountVal, 0, responseSpecError);
     }
 
     @Test
@@ -191,6 +190,11 @@ public class LoanTransactionChargebackTest {
         assertNotNull(getLoansLoanIdResponse);
 
         loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        GetDelinquencyRangesResponse delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
+        assertNotNull(delinquencyRange);
+        log.info("Loan Delinquency Range is {}", delinquencyRange.getClassification());
+
         GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = getLoansLoanIdResponse.getRepaymentSchedule();
         log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
         assertEquals(2, getLoanRepaymentSchedule.getPeriods().size());
@@ -207,7 +211,8 @@ public class LoanTransactionChargebackTest {
 
         reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
 
-        Integer chargebackTransactionId = applyChargebackTransaction(loanId, transactionId, "500.00", 0, responseSpec);
+        Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "500.00", 0,
+                responseSpec);
 
         reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("500.00"));
@@ -232,7 +237,7 @@ public class LoanTransactionChargebackTest {
             }
         }
 
-        chargebackTransactionId = applyChargebackTransaction(loanId, transactionId, "300.00", 0, responseSpec);
+        chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "300.00", 0, responseSpec);
 
         reviewLoanTransactionRelations(loanId, transactionId, 2, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("800.00"));
@@ -241,6 +246,10 @@ public class LoanTransactionChargebackTest {
         assertNotNull(getLoansLoanIdResponse);
         loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.active");
 
+        delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
+        assertNull(delinquencyRange);
+        log.info("Loan Delinquency Range is null {}", (delinquencyRange == null));
+
         loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf("800.00"));
 
         // N+1 Scenario -- Remains the same periods number
@@ -280,7 +289,8 @@ public class LoanTransactionChargebackTest {
 
         reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
 
-        final Integer chargebackTransactionId = applyChargebackTransaction(loanId, transactionId, "200.00", 0, responseSpec);
+        final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "200.00", 0,
+                responseSpec);
 
         reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("100.00"));
@@ -290,6 +300,10 @@ public class LoanTransactionChargebackTest {
         loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.active");
 
         loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf("100.00"));
+
+        final GetDelinquencyRangesResponse delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
+        assertNull(delinquencyRange);
+        log.info("Loan Delinquency Range is null {}", (delinquencyRange == null));
     }
 
     @Test
@@ -314,7 +328,8 @@ public class LoanTransactionChargebackTest {
 
         reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
 
-        final Integer chargebackTransactionId = applyChargebackTransaction(loanId, transactionId, "100.00", 0, responseSpec);
+        final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "100.00", 0,
+                responseSpec);
 
         reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("0.00"));
@@ -348,8 +363,11 @@ public class LoanTransactionChargebackTest {
 
         reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
 
-        final Integer chargebackTransactionId = applyChargebackTransaction(loanId, transactionId, "50.00", 0, responseSpec);
-
+        GetDelinquencyRangesResponse delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
+        assertNull(delinquencyRange);
+        log.info("Loan Delinquency Range is null {}", (delinquencyRange == null));
+        final Integer chargebackTransactionId = loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "50.00", 0,
+                responseSpec);
         reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, chargebackTransactionId, 0, Double.valueOf("0.00"));
 
@@ -357,6 +375,10 @@ public class LoanTransactionChargebackTest {
         assertNotNull(getLoansLoanIdResponse);
         loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse, "loanStatusType.overpaid");
 
+        delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
+        assertNull(delinquencyRange);
+        log.info("Loan Delinquency Range is null {}", (delinquencyRange == null));
+
         loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf("0.00"));
     }
 
@@ -383,38 +405,56 @@ public class LoanTransactionChargebackTest {
         // First round, empty array
         reviewLoanTransactionRelations(loanId, transactionId, 0, Double.valueOf("0.00"));
 
-        applyChargebackTransaction(loanId, transactionId, "200.00", 0, responseSpec);
+        loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "200.00", 0, responseSpec);
 
+        Double expectedAmount = Double.valueOf("200.00");
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf("200.00"));
+        loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, expectedAmount);
 
-        evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, Double.valueOf("200.00"));
+        loanTransactionHelper.evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, expectedAmount);
+        loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
+        DelinquencyBucketsHelper.evaluateLoanCollectionData(getLoansLoanIdResponse, 0, expectedAmount);
 
         // Second round, array size equal to 1
         reviewLoanTransactionRelations(loanId, transactionId, 1, Double.valueOf("0.00"));
 
-        applyChargebackTransaction(loanId, transactionId, "300.00", 1, responseSpec);
+        loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "300.00", 1, responseSpec);
 
+        expectedAmount = Double.valueOf("500.00");
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf("500.00"));
+        loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, expectedAmount);
 
-        evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, Double.valueOf("500.00"));
+        loanTransactionHelper.evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, expectedAmount);
+        DelinquencyBucketsHelper.evaluateLoanCollectionData(getLoansLoanIdResponse, 0, expectedAmount);
 
         // Third round, array size equal to 2
         reviewLoanTransactionRelations(loanId, transactionId, 2, Double.valueOf("0.00"));
 
-        applyChargebackTransaction(loanId, transactionId, "500.00", 0, responseSpec);
+        loanTransactionHelper.applyChargebackTransaction(loanId, transactionId, "500.00", 0, responseSpec);
 
+        expectedAmount = Double.valueOf("1000.00");
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
-        loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, Double.valueOf("1000.00"));
+        loanTransactionHelper.validateLoanPrincipalOustandingBalance(getLoansLoanIdResponse, expectedAmount);
 
-        evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, Double.valueOf("1000.00"));
+        loanTransactionHelper.evaluateLoanSummaryAdjustments(getLoansLoanIdResponse, expectedAmount);
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        DelinquencyBucketsHelper.evaluateLoanCollectionData(getLoansLoanIdResponse, 0, expectedAmount);
     }
 
     private Integer createAccounts(final Integer daysToSubtract, final Integer numberOfRepayments) {
+        // Delinquency Bucket
+        final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketId);
+
         // Client and Loan account creation
         final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, null);
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
+                delinquencyBucketId);
+        assertNotNull(getLoanProductsProductResponse);
+        log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
+        assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
 
         // Older date to have more than one overdue installment
         final LocalDate transactionDate = this.todaysDate.minusDays(daysToSubtract + (30 * (numberOfRepayments - 1)));
@@ -424,16 +464,6 @@ public class LoanTransactionChargebackTest {
                 operationDate, amountVal, numberOfRepayments.toString());
     }
 
-    private String createChargebackPayload(final String transactionAmount, final Integer paymentTypeId) {
-        final HashMap<String, Object> map = new HashMap<>();
-        map.put("transactionAmount", transactionAmount);
-        map.put("paymentTypeId", paymentTypeId);
-        map.put("locale", CommonConstants.LOCALE);
-        final String chargebackPayload = new Gson().toJson(map);
-        log.info("{}", chargebackPayload);
-        return chargebackPayload;
-    }
-
     private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
             final Integer delinquencyBucketId) {
         final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId);
@@ -457,21 +487,6 @@ public class LoanTransactionChargebackTest {
         return loanId;
     }
 
-    private Integer applyChargebackTransaction(final Integer loanId, final Integer transactionId, final String amount,
-            final Integer paymentTypeIdx, ResponseSpecification responseSpec) {
-        List<GetPaymentTypesResponse> paymentTypeList = PaymentTypeHelper.getSystemPaymentType(this.requestSpec, this.responseSpec);
-        assertTrue(!paymentTypeList.isEmpty());
-
-        final String payload = createChargebackPayload(amount, paymentTypeList.get(paymentTypeIdx).getId());
-        log.info("Loan Chargeback: {}", payload);
-        PostLoansLoanIdTransactionsTransactionIdResponse postLoansTransactionCommandResponse = loanTransactionHelper
-                .applyLoanTransactionCommand(loanId, transactionId, "chargeback", payload, responseSpec);
-        assertNotNull(postLoansTransactionCommandResponse);
-
-        log.info("Loan Chargeback Id: {}", postLoansTransactionCommandResponse.getResourceId());
-        return postLoansTransactionCommandResponse.getResourceId();
-    }
-
     private void reviewLoanTransactionRelations(final Integer loanId, final Integer transactionId, final Integer expectedSize,
             final Double outstandingBalance) {
         log.info("Loan Transaction Id: {} {}", loanId, transactionId);
@@ -487,13 +502,4 @@ public class LoanTransactionChargebackTest {
         assertEquals(outstandingBalance, getLoansTransactionResponse.getOutstandingLoanBalance());
     }
 
-    private void evaluateLoanSummaryAdjustments(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
-        // Evaluate The Loan Summary Principal Adjustments
-        GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
-        if (getLoansLoanIdSummary != null) {
-            log.info("Loan with Principal Adjustments {} expected {}", getLoansLoanIdSummary.getPrincipalAdjustments(), amountExpected);
-            assertEquals(amountExpected, getLoansLoanIdSummary.getPrincipalAdjustments());
-        }
-    }
-
 }
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index df68df1c1..a0a624ae6 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -20,6 +20,7 @@ package org.apache.fineract.integrationtests.common.loans;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import com.google.common.reflect.TypeToken;
@@ -43,11 +44,13 @@ import javax.ws.rs.core.MediaType;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdCollectionData;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdSummary;
 import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
+import org.apache.fineract.client.models.GetPaymentTypesResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
@@ -55,6 +58,7 @@ import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionI
 import org.apache.fineract.client.models.PutLoansLoanIdResponse;
 import org.apache.fineract.client.util.JSON;
 import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ss.usermodel.Workbook;
@@ -1155,12 +1159,19 @@ public class LoanTransactionHelper {
         if (getLoanRepaymentSchedule != null) {
             log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
             for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
-                log.info("Period number {} for due date {} and outstanding {}", period.getPeriod(), period.getDueDate(),
-                        period.getTotalOutstandingForPeriod());
+                log.info("Period number {} for due date {} and outstanding {} {}", period.getPeriod(), period.getDueDate(),
+                        period.getTotalOutstandingForPeriod(), period.getComplete());
             }
         }
     }
 
+    public void printDelinquencyData(GetLoansLoanIdResponse getLoansLoanIdResponse) {
+        GetLoansLoanIdCollectionData getLoansLoanIdCollectionData = getLoansLoanIdResponse.getDelinquent();
+        if (getLoansLoanIdCollectionData != null) {
+            log.info("Loan Delinquency {}", getLoansLoanIdCollectionData.toString());
+        }
+    }
+
     public void validateLoanStatus(GetLoansLoanIdResponse getLoansLoanIdResponse, final String statusCodeExpected) {
         final String statusCode = getLoansLoanIdResponse.getStatus().getCode();
         log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode);
@@ -1192,4 +1203,46 @@ public class LoanTransactionHelper {
         }
     }
 
+    public Integer applyChargebackTransaction(final Integer loanId, final Integer transactionId, final String amount,
+            final Integer paymentTypeIdx, ResponseSpecification responseSpec) {
+        List<GetPaymentTypesResponse> paymentTypeList = PaymentTypeHelper.getSystemPaymentType(this.requestSpec, this.responseSpec);
+        assertTrue(!paymentTypeList.isEmpty());
+
+        final String payload = createChargebackPayload(amount, paymentTypeList.get(paymentTypeIdx).getId());
+        log.info("Loan Chargeback: {}", payload);
+        PostLoansLoanIdTransactionsTransactionIdResponse postLoansTransactionCommandResponse = applyLoanTransactionCommand(loanId,
+                transactionId, "chargeback", payload, responseSpec);
+        assertNotNull(postLoansTransactionCommandResponse);
+
+        log.info("Loan Chargeback Id: {}", postLoansTransactionCommandResponse.getResourceId());
+        return postLoansTransactionCommandResponse.getResourceId();
+    }
+
+    public void reviewLoanTransactionRelations(final Integer loanId, final Integer transactionId, final Integer expectedSize) {
+        GetLoansLoanIdTransactionsTransactionIdResponse getLoansTransactionResponse = getLoanTransaction(loanId, transactionId);
+        assertNotNull(getLoansTransactionResponse);
+        assertNotNull(getLoansTransactionResponse.getTransactionRelations());
+        assertEquals(expectedSize, getLoansTransactionResponse.getTransactionRelations().size());
+        log.info("Loan with {} Chargeback Transactions", getLoansTransactionResponse.getTransactionRelations().size());
+    }
+
+    public void evaluateLoanSummaryAdjustments(GetLoansLoanIdResponse getLoansLoanIdResponse, Double amountExpected) {
+        // Evaluate The Loan Summary Principal Adjustments
+        GetLoansLoanIdSummary getLoansLoanIdSummary = getLoansLoanIdResponse.getSummary();
+        if (getLoansLoanIdSummary != null) {
+            log.info("Loan with Principal Adjustments {} expected {}", getLoansLoanIdSummary.getPrincipalAdjustments(), amountExpected);
+            assertEquals(amountExpected, getLoansLoanIdSummary.getPrincipalAdjustments());
+        }
+    }
+
+    private String createChargebackPayload(final String transactionAmount, final Integer paymentTypeId) {
+        final HashMap<String, Object> map = new HashMap<>();
+        map.put("transactionAmount", transactionAmount);
+        map.put("paymentTypeId", paymentTypeId);
+        map.put("locale", CommonConstants.LOCALE);
+        final String chargebackPayload = new Gson().toJson(map);
+        log.info("{}", chargebackPayload);
+        return chargebackPayload;
+    }
+
 }
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java
index 7027e847a..7b5810b1d 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyBucketsHelper.java
@@ -18,6 +18,10 @@
  */
 package org.apache.fineract.integrationtests.common.products;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import com.google.gson.Gson;
 import com.linecorp.armeria.internal.shaded.guava.reflect.TypeToken;
 import io.restassured.specification.RequestSpecification;
@@ -25,22 +29,24 @@ import io.restassured.specification.ResponseSpecification;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.HashMap;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.DeleteDelinquencyBucketResponse;
 import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdCollectionData;
+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.PutDelinquencyBucketResponse;
 import org.apache.fineract.client.util.JSON;
 import org.apache.fineract.integrationtests.common.Utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
+@Slf4j
 public class DelinquencyBucketsHelper {
 
     private static final String DELINQUENCY_BUCKETS_URL = "/fineract-provider/api/v1/delinquency/buckets";
     private static final Gson GSON = new JSON().getGson();
 
-    private static final Logger LOG = LoggerFactory.getLogger(DelinquencyBucketsHelper.class);
-
     protected DelinquencyBucketsHelper() {}
 
     public static ArrayList<GetDelinquencyBucketsResponse> getDelinquencyBuckets(final RequestSpecification requestSpec,
@@ -60,7 +66,7 @@ public class DelinquencyBucketsHelper {
 
     public static PostDelinquencyBucketResponse createDelinquencyBucket(final RequestSpecification requestSpec,
             final ResponseSpecification responseSpec, final String json) {
-        LOG.info("JSON: {}", json);
+        log.info("JSON: {}", json);
         final String response = Utils.performServerPost(requestSpec, responseSpec, DELINQUENCY_BUCKETS_URL + "?" + Utils.TENANT_IDENTIFIER,
                 json, null);
         return GSON.fromJson(response, PostDelinquencyBucketResponse.class);
@@ -68,7 +74,7 @@ public class DelinquencyBucketsHelper {
 
     public static PutDelinquencyBucketResponse updateDelinquencyBucket(final RequestSpecification requestSpec,
             final ResponseSpecification responseSpec, final Integer resourceId, final String json) {
-        LOG.info("JSON: {}", json);
+        log.info("JSON: {}", json);
         final String response = Utils.performServerPut(requestSpec, responseSpec,
                 DELINQUENCY_BUCKETS_URL + "/" + resourceId + "?" + Utils.TENANT_IDENTIFIER, json, null);
         return GSON.fromJson(response, PutDelinquencyBucketResponse.class);
@@ -88,4 +94,52 @@ public class DelinquencyBucketsHelper {
         return new Gson().toJson(map);
     }
 
+    public static Integer createDelinquencyBucket(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
+        ArrayList<Integer> rangeIds = new ArrayList<>();
+
+        // First Range
+        String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+        PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
+                jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
+        GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                delinquencyRangeResponse.getResourceId());
+
+        // Second Range
+        delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+        rangeIds.add(delinquencyRangeResponse.getResourceId());
+        range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec, delinquencyRangeResponse.getResourceId());
+
+        String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+        PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                responseSpec, jsonBucket);
+        assertNotNull(delinquencyBucketResponse);
+
+        return delinquencyBucketResponse.getResourceId();
+    }
+
+    public static void evaluateLoanCollectionData(GetLoansLoanIdResponse getLoansLoanIdResponse, Integer pastDueDays,
+            Double amountExpected) {
+        GetLoansLoanIdCollectionData getCollectionData = getLoansLoanIdResponse.getDelinquent();
+        if (getCollectionData != null) {
+            log.info("Loan Delinquency Data in Days {} and Amount {}", getCollectionData.getPastDueDays(),
+                    getCollectionData.getDelinquentAmount());
+            assertEquals(pastDueDays, getCollectionData.getPastDueDays());
+            assertEquals(amountExpected, getCollectionData.getDelinquentAmount());
+        } else {
+            log.info("Loan Delinquency Data is null");
+        }
+
+        GetDelinquencyRangesResponse delinquencyRange = getLoansLoanIdResponse.getDelinquencyRange();
+        if (delinquencyRange != null) {
+            log.info("Loan Delinquency Classification is {} : ({} - {}) {}", delinquencyRange.getClassification(),
+                    delinquencyRange.getMinimumAgeDays(), delinquencyRange.getMaximumAgeDays(), pastDueDays);
+            assertTrue(delinquencyRange.getMinimumAgeDays() <= pastDueDays);
+            assertTrue(delinquencyRange.getMaximumAgeDays() >= pastDueDays);
+        } else {
+            log.info("Loan Delinquency Classification is null");
+        }
+    }
+
 }
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyRangesHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyRangesHelper.java
index 0a7af2d48..32a7cef4a 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyRangesHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/products/DelinquencyRangesHelper.java
@@ -85,7 +85,7 @@ public class DelinquencyRangesHelper {
 
     public static String getAsJSON(int minimumAgeDays, int maximumAgeDays) {
         final HashMap<String, Object> map = new HashMap<>();
-        map.put("classification", Utils.randomNameGenerator("Delinquency_", 4));
+        map.put("classification", Utils.randomNameGenerator("Delinquency__" + minimumAgeDays + "_" + maximumAgeDays + "__", 4));
         map.put("minimumAgeDays", minimumAgeDays);
         map.put("maximumAgeDays", maximumAgeDays);
         map.put("locale", "en");