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

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


The following commit(s) were added to refs/heads/develop by this push:
     new 8cea837a5 
FINERACT-1992-FINERACT-Installment-level-delinquency-calculation
8cea837a5 is described below

commit 8cea837a5b8071ab8e59659b16f9dc0f85fbec40
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Thu Oct 26 20:25:07 2023 +0530

    FINERACT-1992-FINERACT-Installment-level-delinquency-calculation
---
 .../tenant/module/loan/module-changelog-master.xml |   1 +
 ...9_refactor_loan_Installment_delinquency_tag.xml |  31 +++
 .../LoanInstallmentDelinquencyTagData.java}        |  27 ++-
 .../domain/LoanInstallmentDelinquencyTag.java      |  80 +++++++
 .../LoanInstallmentDelinquencyTagRepository.java   |  52 +++++
 .../service/DelinquencyReadPlatformService.java    |   3 +
 .../DelinquencyReadPlatformServiceImpl.java        |   8 +
 .../DelinquencyWritePlatformServiceImpl.java       | 133 +++++++++++-
 .../service/LoanDelinquencyDomainService.java      |   3 +
 .../service/LoanDelinquencyDomainServiceImpl.java  | 233 ++++++++++++++++-----
 .../starter/DelinquencyConfiguration.java          |  12 +-
 .../data/LoanDelinquencyData.java}                 |  25 +--
 ...cyWritePlatformServiceRangeChangeEventTest.java | 161 +++++++++++++-
 .../LoanDelinquencyDomainServiceTest.java          |  93 ++++++++
 14 files changed, 782 insertions(+), 80 deletions(-)

diff --git 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
index 27e5aea91..56a18e16e 100644
--- 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
+++ 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
@@ -31,4 +31,5 @@
   <include relativeToChangelogFile="true" 
file="parts/1006_add_downpayment_transaction_enum_permissions.xml"/>
   <include relativeToChangelogFile="true" 
file="parts/1007_add_loan_product_schedule_extension_for_down_payment_configuration.xml"/>
   <include relativeToChangelogFile="true" 
file="parts/1008_add_loan_installment_delinquency_tag.xml"/>
+  <include relativeToChangelogFile="true" 
file="parts/1009_refactor_loan_Installment_delinquency_tag.xml"/>
 </databaseChangeLog>
diff --git 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1009_refactor_loan_Installment_delinquency_tag.xml
 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1009_refactor_loan_Installment_delinquency_tag.xml
new file mode 100644
index 000000000..62df11440
--- /dev/null
+++ 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1009_refactor_loan_Installment_delinquency_tag.xml
@@ -0,0 +1,31 @@
+<?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 author="fineract" id="1">
+        <renameTable oldTableName="m_loan_installment_delinquency_tag_history" 
newTableName="m_loan_installment_delinquency_tag"/>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <dropForeignKeyConstraint 
baseTableName="m_loan_installment_delinquency_tag" 
constraintName="FK_m_delinquency_installment_tags_installment" />
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/data/LoanInstallmentDelinquencyTagData.java
similarity index 62%
copy from 
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/data/LoanInstallmentDelinquencyTagData.java
index 5cd19cb5f..860156951 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/data/LoanInstallmentDelinquencyTagData.java
@@ -16,19 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.delinquency.service;
+package org.apache.fineract.portfolio.delinquency.data;
 
-import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
-import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import java.math.BigDecimal;
 
-public interface LoanDelinquencyDomainService {
+public interface LoanInstallmentDelinquencyTagData {
 
-    /**
-     * This method is to calculate the Overdue date and other properties, If 
the loan is overdue or If there is some
-     * Charge back transaction
-     *
-     * @param loan
-     */
-    CollectionData getOverdueCollectionData(Loan loan);
+    InstallmentDelinquencyRange getDelinquencyRange();
 
+    BigDecimal getOutstandingAmount();
+
+    interface InstallmentDelinquencyRange {
+
+        Long getId();
+
+        String getClassification();
+
+        Integer getMinimumAgeDays();
+
+        Integer getMaximumAgeDays();
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanInstallmentDelinquencyTag.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanInstallmentDelinquencyTag.java
new file mode 100644
index 000000000..248c49d51
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanInstallmentDelinquencyTag.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import jakarta.persistence.Version;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import 
org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name = "m_loan_installment_delinquency_tag")
+public class LoanInstallmentDelinquencyTag extends 
AbstractAuditableWithUTCDateTimeCustom {
+
+    @ManyToOne
+    @JoinColumn(name = "delinquency_range_id", nullable = false)
+    private DelinquencyRange delinquencyRange;
+
+    @ManyToOne
+    @JoinColumn(name = "loan_id", nullable = false)
+    private Loan loan;
+
+    @ManyToOne
+    @JoinColumn(name = "installment_id", nullable = false)
+    private LoanRepaymentScheduleInstallment installment;
+
+    @Column(name = "addedon_date", nullable = false)
+    private LocalDate addedOnDate;
+
+    @Column(name = "liftedon_date", nullable = true)
+    private LocalDate liftedOnDate;
+
+    @Column(name = "first_overdue_date", nullable = false)
+    private LocalDate firstOverdueDate;
+
+    @Column(name = "outstanding_amount", scale = 6, precision = 19)
+    private BigDecimal outstandingAmount;
+
+    @Version
+    private Long version;
+
+    public LoanInstallmentDelinquencyTag(DelinquencyRange delinquencyRange, 
Loan loan, LoanRepaymentScheduleInstallment installment,
+            LocalDate addedOnDate, LocalDate liftedOnDate, LocalDate 
firstOverdueDate, BigDecimal outstandingAmount) {
+        this.delinquencyRange = delinquencyRange;
+        this.loan = loan;
+        this.installment = installment;
+        this.addedOnDate = addedOnDate;
+        this.liftedOnDate = liftedOnDate;
+        this.firstOverdueDate = firstOverdueDate;
+        this.outstandingAmount = outstandingAmount;
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanInstallmentDelinquencyTagRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanInstallmentDelinquencyTagRepository.java
new file mode 100644
index 000000000..5729fdec6
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/domain/LoanInstallmentDelinquencyTagRepository.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.delinquency.domain;
+
+import java.util.List;
+import java.util.Optional;
+import 
org.apache.fineract.portfolio.delinquency.data.LoanInstallmentDelinquencyTagData;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+public interface LoanInstallmentDelinquencyTagRepository
+        extends JpaRepository<LoanInstallmentDelinquencyTag, Long>, 
JpaSpecificationExecutor<LoanInstallmentDelinquencyTag> {
+
+    Optional<LoanInstallmentDelinquencyTag> findByLoanAndInstallment(Loan 
loan, LoanRepaymentScheduleInstallment installment);
+
+    @Query("select i from LoanInstallmentDelinquencyTag i where i.loan.id = 
:loanId")
+    List<LoanInstallmentDelinquencyTag> findByLoanId(@Param("loanId") Long 
loanId);
+
+    // Fetching Installment Delinquency range and outstanding amount
+    @Query("select i.delinquencyRange, i.outstandingAmount from 
LoanInstallmentDelinquencyTag i where i.loan.id = :loanId")
+    List<LoanInstallmentDelinquencyTagData> 
findInstallmentDelinquencyTags(@Param("loanId") Long loanId);
+
+    @Modifying(flushAutomatically = true)
+    @Query("delete from LoanInstallmentDelinquencyTag i where i.loan.id = 
:loanId")
+    void deleteAllLoanInstallmentsTags(@Param("loanId") Long loanId);
+
+    @Modifying(flushAutomatically = true)
+    @Query("delete from LoanInstallmentDelinquencyTag i where i.id IN :tagIds")
+    void deleteAllLoanInstallmentsTagsByIds(@Param("tagIds") List<Long> 
tagIds);
+
+}
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 b1c2dad11..9f3510cce 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.delinquency.data.LoanInstallmentDelinquencyTagData;
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 
 public interface DelinquencyReadPlatformService {
@@ -40,4 +41,6 @@ public interface DelinquencyReadPlatformService {
 
     CollectionData calculateLoanCollectionData(Long loanId);
 
+    Collection<LoanInstallmentDelinquencyTagData> 
retrieveLoanInstallmentsCurrentDelinquencyTag(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 c98b49327..7e1d9c145 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
@@ -25,12 +25,14 @@ import lombok.RequiredArgsConstructor;
 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.delinquency.data.LoanInstallmentDelinquencyTagData;
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
 import 
org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
 import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
 import 
org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper;
@@ -52,6 +54,7 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
     private final LoanDelinquencyTagMapper mapperLoanDelinquencyTagHistory;
     private final LoanRepository loanRepository;
     private final LoanDelinquencyDomainService loanDelinquencyDomainService;
+    private final LoanInstallmentDelinquencyTagRepository 
repositoryLoanInstallmentDelinquencyTag;
 
     @Override
     public Collection<DelinquencyRangeData> retrieveAllDelinquencyRanges() {
@@ -126,4 +129,9 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
         return collectionData;
     }
 
+    @Override
+    public Collection<LoanInstallmentDelinquencyTagData> 
retrieveLoanInstallmentsCurrentDelinquencyTag(Long loanId) {
+        return 
repositoryLoanInstallmentDelinquencyTag.findInstallmentDelinquencyTags(loanId);
+    }
+
 }
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 883a47808..0520c36d1 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
@@ -46,13 +46,17 @@ import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistory;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTag;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
 import 
org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketAgesOverlapedException;
 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.CollectionData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
 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.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
 
@@ -71,6 +75,7 @@ public class DelinquencyWritePlatformServiceImpl implements 
DelinquencyWritePlat
     private final LoanProductRepository loanProductRepository;
     private final BusinessEventNotifierService businessEventNotifierService;
     private final LoanDelinquencyDomainService loanDelinquencyDomainService;
+    private final LoanInstallmentDelinquencyTagRepository 
loanInstallmentDelinquencyTagRepository;
 
     @Override
     public CommandProcessingResult createDelinquencyRange(JsonCommand command) 
{
@@ -158,8 +163,17 @@ public class DelinquencyWritePlatformServiceImpl 
implements DelinquencyWritePlat
         final Loan loan = 
this.loanRepository.findOneWithNotFoundDetection(loanId);
         final DelinquencyBucket delinquencyBucket = 
loan.getLoanProduct().getDelinquencyBucket();
         if (delinquencyBucket != null) {
-            final CollectionData collectionData = 
loanDelinquencyDomainService.getOverdueCollectionData(loan);
+            final LoanDelinquencyData loanDelinquencyData = 
loanDelinquencyDomainService.getLoanDelinquencyData(loan);
+            // loan delinquent data
+            final CollectionData collectionData = 
loanDelinquencyData.getLoanCollectionData();
+            // loan installments delinquent data
+            final Map<Long, CollectionData> installmentsCollectionData = 
loanDelinquencyData.getLoanInstallmentsCollectionData();
+            // delinquency for loan
             changes = lookUpDelinquencyRange(loan, delinquencyBucket, 
collectionData.getDelinquentDays());
+            // delinquency for installments
+            if (installmentsCollectionData.size() > 0) {
+                applyDelinquencyDetailsForLoanInstallments(loan, 
delinquencyBucket, installmentsCollectionData);
+            }
         }
         return new 
CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loan.getId())
                 
.withEntityExternalId(loan.getExternalId()).with(changes).build();
@@ -170,15 +184,27 @@ public class DelinquencyWritePlatformServiceImpl 
implements DelinquencyWritePlat
         final Loan loan = loanDelinquencyData.getLoan();
         if (loan.hasDelinquencyBucket()) {
             final DelinquencyBucket delinquencyBucket = 
loan.getLoanProduct().getDelinquencyBucket();
-            final CollectionData collectionData = 
loanDelinquencyDomainService.getOverdueCollectionData(loan);
+            final LoanDelinquencyData loanDelinquentData = 
loanDelinquencyDomainService.getLoanDelinquencyData(loan);
+            // loan delinquent data
+            final CollectionData collectionData = 
loanDelinquentData.getLoanCollectionData();
+            // loan installments delinquent data
+            final Map<Long, CollectionData> installmentsCollectionData = 
loanDelinquentData.getLoanInstallmentsCollectionData();
             log.debug("Delinquency {}", collectionData);
+            // delinquency for loan
             lookUpDelinquencyRange(loan, delinquencyBucket, 
collectionData.getDelinquentDays());
+            // delinquency for installments
+            if (installmentsCollectionData.size() > 0) {
+                applyDelinquencyDetailsForLoanInstallments(loan, 
delinquencyBucket, installmentsCollectionData);
+            }
         }
     }
 
     @Override
     public void removeDelinquencyTagToLoan(final Loan loan) {
         setLoanDelinquencyTag(loan, null);
+        if (loan.isEnableInstallmentLevelDelinquency()) {
+            cleanLoanInstallmentsDelinquencyTags(loan);
+        }
     }
 
     @Override
@@ -382,4 +408,107 @@ public class DelinquencyWritePlatformServiceImpl 
implements DelinquencyWritePlat
         return ranges;
     }
 
+    private void applyDelinquencyDetailsForLoanInstallments(final Loan loan, 
final DelinquencyBucket delinquencyBucket,
+            final Map<Long, CollectionData> installmentsCollectionData) {
+        for (LoanRepaymentScheduleInstallment installment : 
loan.getRepaymentScheduleInstallments()) {
+            if (installmentsCollectionData.containsKey(installment.getId())) {
+                setInstallmentDelinquencyDetails(loan, installment, 
delinquencyBucket, installmentsCollectionData.get(installment.getId()));
+            }
+        }
+        // remove tags for non existing installments that got deleted due to 
re-schedule
+        removeDelinquencyTagsForNonExistingInstallments(loan.getId());
+    }
+
+    private void setInstallmentDelinquencyDetails(final Loan loan, final 
LoanRepaymentScheduleInstallment installment,
+            final DelinquencyBucket delinquencyBucket, final CollectionData 
installmentDelinquencyData) {
+        DelinquencyRange delinquencyRangeForInstallment = 
getInstallmentDelinquencyRange(delinquencyBucket,
+                installmentDelinquencyData.getDelinquentDays());
+        setDelinquencyDetailsForInstallment(loan, installment, 
installmentDelinquencyData, delinquencyRangeForInstallment);
+    }
+
+    private DelinquencyRange getInstallmentDelinquencyRange(final 
DelinquencyBucket delinquencyBucket, Long overDueDays) {
+        DelinquencyRange delinquencyRangeForInstallment = null;
+        if (overDueDays > 0) {
+            // Sort the ranges based on the minAgeDays
+            final List<DelinquencyRange> ranges = 
sortDelinquencyRangesByMinAge(delinquencyBucket.getRanges());
+            for (final DelinquencyRange delinquencyRange : ranges) {
+                if (delinquencyRange.getMaximumAgeDays() == null) { // Last 
Range in the Bucket
+                    if (delinquencyRange.getMinimumAgeDays() <= overDueDays) {
+                        delinquencyRangeForInstallment = delinquencyRange;
+                        break;
+                    }
+                } else {
+                    if (delinquencyRange.getMinimumAgeDays() <= overDueDays && 
delinquencyRange.getMaximumAgeDays() >= overDueDays) {
+                        delinquencyRangeForInstallment = delinquencyRange;
+                        break;
+                    }
+                }
+            }
+
+        }
+        return delinquencyRangeForInstallment;
+    }
+
+    private void setDelinquencyDetailsForInstallment(final Loan loan, final 
LoanRepaymentScheduleInstallment installment,
+            CollectionData installmentDelinquencyData, final DelinquencyRange 
delinquencyRangeForInstallment) {
+        List<LoanInstallmentDelinquencyTag> installmentDelinquencyTags = new 
ArrayList<>();
+        LocalDate delinquencyCalculationDate = 
DateUtils.getBusinessLocalDate();
+
+        LoanInstallmentDelinquencyTag previousInstallmentDelinquencyTag = 
loanInstallmentDelinquencyTagRepository
+                .findByLoanAndInstallment(loan, installment).orElse(null);
+
+        if (delinquencyRangeForInstallment == null) {
+            // if currentInstallmentDelinquencyTag exists and range is null, 
installment is out of delinquency, delete
+            // delinquency details
+            if (previousInstallmentDelinquencyTag != null) {
+                // event installment out of delinquency
+                
loanInstallmentDelinquencyTagRepository.delete(previousInstallmentDelinquencyTag);
+            }
+        } else {
+            LoanInstallmentDelinquencyTag installmentDelinquency = null;
+            if (previousInstallmentDelinquencyTag != null) {
+                if 
(!previousInstallmentDelinquencyTag.getDelinquencyRange().getId().equals(delinquencyRangeForInstallment.getId()))
 {
+                    // if current delinquency range exists and there is range 
change, delete previous delinquency
+                    // details and add new range details
+                    installmentDelinquency = new 
LoanInstallmentDelinquencyTag(delinquencyRangeForInstallment, loan, installment,
+                            delinquencyCalculationDate, null, 
previousInstallmentDelinquencyTag.getFirstOverdueDate(),
+                            installmentDelinquencyData.getDelinquentAmount());
+                    
loanInstallmentDelinquencyTagRepository.delete(previousInstallmentDelinquencyTag);
+                    // event installment delinquency range change
+                }
+            } else {
+                // add new range, first time delinquent
+                installmentDelinquency = new 
LoanInstallmentDelinquencyTag(delinquencyRangeForInstallment, loan, installment,
+                        delinquencyCalculationDate, null, 
installmentDelinquencyData.getDelinquentDate(),
+                        installmentDelinquencyData.getDelinquentAmount());
+                // event installment delinquent
+            }
+
+            if (installmentDelinquency != null) {
+                installmentDelinquencyTags.add(installmentDelinquency);
+            }
+
+        }
+
+        if (installmentDelinquencyTags.size() > 0) {
+            
loanInstallmentDelinquencyTagRepository.saveAllAndFlush(installmentDelinquencyTags);
+        }
+
+    }
+
+    private void cleanLoanInstallmentsDelinquencyTags(Loan loan) {
+        
loanInstallmentDelinquencyTagRepository.deleteAllLoanInstallmentsTags(loan.getId());
+    }
+
+    private void removeDelinquencyTagsForNonExistingInstallments(Long loanId) {
+        List<LoanInstallmentDelinquencyTag> 
currentLoanInstallmentDelinquencyTags = loanInstallmentDelinquencyTagRepository
+                .findByLoanId(loanId);
+        if (currentLoanInstallmentDelinquencyTags != null && 
currentLoanInstallmentDelinquencyTags.size() > 0) {
+            List<Long> loanInstallmentTagsForDelete = 
currentLoanInstallmentDelinquencyTags.stream()
+                    .filter(tag -> tag.getInstallment() == null).map(tag -> 
tag.getId()).toList();
+            if (loanInstallmentTagsForDelete.size() > 0) {
+                
loanInstallmentDelinquencyTagRepository.deleteAllLoanInstallmentsTagsByIds(loanInstallmentTagsForDelete);
+            }
+        }
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
index 5cd19cb5f..656ceb6b7 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.portfolio.delinquency.service;
 
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 
 public interface LoanDelinquencyDomainService {
@@ -31,4 +32,6 @@ public interface LoanDelinquencyDomainService {
      */
     CollectionData getOverdueCollectionData(Loan loan);
 
+    LoanDelinquencyData getLoanDelinquencyData(Loan loan);
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
index 343eeabdb..67085cd65 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
@@ -20,12 +20,15 @@ package org.apache.fineract.portfolio.delinquency.service;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -42,15 +45,10 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         final MonetaryCurrency loanCurrency = loan.getCurrency();
         LocalDate overdueSinceDate = null;
         CollectionData collectionData = CollectionData.template();
-        BigDecimal amountAvailable;
         BigDecimal outstandingAmount = BigDecimal.ZERO;
         boolean oldestOverdueInstallment = false;
         boolean overdueSinceDateWasSet = false;
         boolean firstNotYetDueInstallment = false;
-        LoanRepaymentScheduleInstallment latestInstallment = 
loan.getLastLoanRepaymentScheduleInstallment();
-
-        List<LoanTransaction> chargebackTransactions = 
loan.getLoanTransactions(LoanTransaction::isChargeback);
-
         log.debug("Loan id {} with {} installments", loan.getId(), 
loan.getRepaymentScheduleInstallments().size());
         // Get the oldest overdue installment if exists one
         for (LoanRepaymentScheduleInstallment installment : 
loan.getRepaymentScheduleInstallments()) {
@@ -61,61 +59,90 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
                     outstandingAmount = 
outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount());
                     if (!oldestOverdueInstallment) {
                         log.debug("Oldest installment {} {}", 
installment.getInstallmentNumber(), installment.getDueDate());
+                        CollectionData overDueInstallmentDelinquentData = 
calculateDelinquencyDataForOverdueInstallment(loan, installment);
+                        overdueSinceDate = 
overDueInstallmentDelinquentData.getDelinquentDate();
                         oldestOverdueInstallment = true;
-                        overdueSinceDate = installment.getDueDate();
                         overdueSinceDateWasSet = true;
-
-                        amountAvailable = 
installment.getTotalPaid(loanCurrency).getAmount();
-
-                        boolean isLatestInstallment = 
Objects.equals(installment.getId(), latestInstallment.getId());
-                        for (LoanTransaction loanTransaction : 
chargebackTransactions) {
-                            boolean 
isLoanTransactionIsOnOrAfterInstallmentFromDate = DateUtils
-                                    
.isEqual(loanTransaction.getTransactionDate(), installment.getFromDate())
-                                    || 
DateUtils.isAfter(loanTransaction.getTransactionDate(), 
installment.getFromDate());
-                            boolean 
isLoanTransactionIsBeforeNotLastInstallmentDueDate = !isLatestInstallment
-                                    && 
DateUtils.isBefore(loanTransaction.getTransactionDate(), 
installment.getDueDate());
-                            boolean 
isLoanTransactionIsOnOrBeforeLastInstallmentDueDate = isLatestInstallment
-                                    && 
!DateUtils.isAfter(loanTransaction.getTransactionDate(), 
installment.getDueDate());
-                            if 
(isLoanTransactionIsOnOrAfterInstallmentFromDate && 
(isLoanTransactionIsBeforeNotLastInstallmentDueDate
-                                    || 
isLoanTransactionIsOnOrBeforeLastInstallmentDueDate)) {
-                                amountAvailable = 
amountAvailable.subtract(loanTransaction.getAmount());
-                                if (amountAvailable.compareTo(BigDecimal.ZERO) 
< 0) {
-                                    overdueSinceDate = 
loanTransaction.getTransactionDate();
-                                    break;
-                                }
-                            }
-                        }
                     }
                 } else if (!firstNotYetDueInstallment) {
                     log.debug("Loan Id: {} with installment {} due date {}", 
loan.getId(), installment.getInstallmentNumber(),
                             installment.getDueDate());
                     firstNotYetDueInstallment = true;
-                    amountAvailable = 
installment.getTotalPaid(loanCurrency).getAmount();
-                    log.debug("Amount available {}", amountAvailable);
-                    for (LoanTransaction loanTransaction : 
chargebackTransactions) {
-                        boolean 
isLoanTransactionIsOnOrAfterInstallmentFromDate = 
!DateUtils.isBefore(loanTransaction.getTransactionDate(),
-                                installment.getFromDate());
-                        boolean isLoanTransactionIsBeforeInstallmentDueDate = 
DateUtils.isBefore(loanTransaction.getTransactionDate(),
-                                installment.getDueDate());
-                        boolean isLoanTransactionIsBeforeBusinessDate = 
DateUtils.isBefore(loanTransaction.getTransactionDate(),
-                                businessDate);
-                        if (isLoanTransactionIsOnOrAfterInstallmentFromDate && 
isLoanTransactionIsBeforeInstallmentDueDate
-                                && isLoanTransactionIsBeforeBusinessDate) {
-                            log.debug("Loan CB Transaction: {} {} {}", 
loanTransaction.getId(), loanTransaction.getTransactionDate(),
-                                    loanTransaction.getAmount());
-                            amountAvailable = 
amountAvailable.subtract(loanTransaction.getAmount());
-                            if (amountAvailable.compareTo(BigDecimal.ZERO) < 0 
&& !overdueSinceDateWasSet) {
-                                overdueSinceDate = 
loanTransaction.getTransactionDate();
-                                overdueSinceDateWasSet = true;
-                            }
-                        }
+                    CollectionData nonOverDueInstallmentDelinquentData = 
calculateDelinquencyDataForNonOverdueInstallment(loan,
+                            installment);
+                    outstandingAmount = 
outstandingAmount.add(nonOverDueInstallmentDelinquentData.getDelinquentAmount());
+                    if (!overdueSinceDateWasSet) {
+                        overdueSinceDate = 
nonOverDueInstallmentDelinquentData.getDelinquentDate();
+                        overdueSinceDateWasSet = true;
                     }
+                }
+            }
+        }
+
+        Integer graceDays = 0;
+        if (loan.getLoanProductRelatedDetail().getGraceOnArrearsAgeing() != 
null) {
+            graceDays = 
loan.getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
+        }
+        log.debug("Loan id {} with overdue since date {} and outstanding 
amount {}", loan.getId(), overdueSinceDate, outstandingAmount);
 
-                    if (amountAvailable.compareTo(BigDecimal.ZERO) < 0) {
-                        outstandingAmount = 
outstandingAmount.add(amountAvailable.abs());
+        Long overdueDays = 0L;
+        if (overdueSinceDate != null) {
+            overdueDays = DateUtils.getDifferenceInDays(overdueSinceDate, 
businessDate);
+            if (overdueDays < 0) {
+                overdueDays = 0L;
+            }
+            collectionData.setPastDueDays(overdueDays);
+            overdueSinceDate = 
overdueSinceDate.plusDays(graceDays.longValue());
+            collectionData.setDelinquentDate(overdueSinceDate);
+        }
+        collectionData.setDelinquentAmount(outstandingAmount);
+        collectionData.setDelinquentDays(0L);
+        Long delinquentDays = overdueDays - graceDays;
+        if (delinquentDays > 0) {
+            collectionData.setDelinquentDays(delinquentDays);
+        }
+
+        log.debug("Result: {}", collectionData.toString());
+        return collectionData;
+    }
+
+    @Override
+    public LoanDelinquencyData getLoanDelinquencyData(final Loan loan) {
+
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        LocalDate overdueSinceDate = null;
+        CollectionData collectionData = CollectionData.template();
+        Map<Long, CollectionData> loanInstallmentsCollectionData = new 
HashMap<>();
+        BigDecimal outstandingAmount = BigDecimal.ZERO;
+        boolean oldestOverdueInstallment = false;
+        boolean overdueSinceDateWasSet = false;
+        boolean firstNotYetDueInstallment = false;
+        log.debug("Loan id {} with {} installments", loan.getId(), 
loan.getRepaymentScheduleInstallments().size());
+        for (LoanRepaymentScheduleInstallment installment : 
loan.getRepaymentScheduleInstallments()) {
+            CollectionData installmentCollectionData = 
CollectionData.template();
+            if (!installment.isObligationsMet()) {
+                installmentCollectionData = 
getInstallmentOverdueCollectionData(loan, installment);
+                outstandingAmount = 
outstandingAmount.add(installmentCollectionData.getDelinquentAmount());
+                // Get the oldest overdue installment if exists
+                if (DateUtils.isBefore(installment.getDueDate(), 
businessDate)) {
+                    if (!oldestOverdueInstallment) {
+                        overdueSinceDate = 
installmentCollectionData.getDelinquentDate();
+                        oldestOverdueInstallment = true;
+                        overdueSinceDateWasSet = true;
+                    }
+                } else if (!firstNotYetDueInstallment) {
+                    firstNotYetDueInstallment = true;
+                    if (!overdueSinceDateWasSet) {
+                        overdueSinceDate = 
installmentCollectionData.getDelinquentDate();
+                        overdueSinceDateWasSet = true;
                     }
                 }
             }
+            // if installment level delinquency enabled add delinquency data 
for installment
+            if (loan.isEnableInstallmentLevelDelinquency()) {
+                loanInstallmentsCollectionData.put(installment.getId(), 
installmentCollectionData);
+            }
+
         }
 
         Integer graceDays = 0;
@@ -140,8 +167,116 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         if (delinquentDays > 0) {
             collectionData.setDelinquentDays(delinquentDays);
         }
+        return new LoanDelinquencyData(collectionData, 
loanInstallmentsCollectionData);
+    }
 
-        log.debug("Result: {}", collectionData.toString());
+    private CollectionData getInstallmentOverdueCollectionData(final Loan 
loan, final LoanRepaymentScheduleInstallment installment) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        LocalDate overdueSinceDate = null;
+        CollectionData collectionData = CollectionData.template();
+        BigDecimal outstandingAmount = BigDecimal.ZERO;
+        if (DateUtils.isBefore(installment.getDueDate(), businessDate)) {
+            // checking overdue installment delinquency data
+            CollectionData overDueInstallmentDelinquentData = 
calculateDelinquencyDataForOverdueInstallment(loan, installment);
+            outstandingAmount = 
outstandingAmount.add(overDueInstallmentDelinquentData.getDelinquentAmount());
+            overdueSinceDate = 
overDueInstallmentDelinquentData.getDelinquentDate();
+
+        } else {
+            // checking non overdue installment for chargeback transactions 
before installment due date and before
+            // business date
+            CollectionData nonOverDueInstallmentDelinquentData = 
calculateDelinquencyDataForNonOverdueInstallment(loan, installment);
+            outstandingAmount = 
outstandingAmount.add(nonOverDueInstallmentDelinquentData.getDelinquentAmount());
+            overdueSinceDate = 
nonOverDueInstallmentDelinquentData.getDelinquentDate();
+        }
+
+        // Grace days are not considered for installment level delinquency 
calculation currently.
+
+        Long overdueDays = 0L;
+        if (overdueSinceDate != null) {
+            // TODO : Changes for considering paused delinquency days for 
overdue days calculation
+            overdueDays = DateUtils.getDifferenceInDays(overdueSinceDate, 
businessDate);
+            if (overdueDays < 0) {
+                overdueDays = 0L;
+            }
+            collectionData.setPastDueDays(overdueDays);
+            collectionData.setDelinquentDate(overdueSinceDate);
+        }
+        collectionData.setDelinquentAmount(outstandingAmount);
+        collectionData.setDelinquentDays(0L);
+        Long delinquentDays = overdueDays;
+        if (delinquentDays > 0) {
+            collectionData.setDelinquentDays(delinquentDays);
+        }
+        return collectionData;
+
+    }
+
+    private CollectionData calculateDelinquencyDataForOverdueInstallment(final 
Loan loan,
+            final LoanRepaymentScheduleInstallment installment) {
+        final MonetaryCurrency loanCurrency = loan.getCurrency();
+        LoanRepaymentScheduleInstallment latestInstallment = 
loan.getLastLoanRepaymentScheduleInstallment();
+        List<LoanTransaction> chargebackTransactions = 
loan.getLoanTransactions(LoanTransaction::isChargeback);
+        LocalDate overdueSinceDate = null;
+        CollectionData collectionData = CollectionData.template();
+        BigDecimal outstandingAmount = BigDecimal.ZERO;
+
+        outstandingAmount = 
outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount());
+        overdueSinceDate = installment.getDueDate();
+        BigDecimal amountAvailable = 
installment.getTotalPaid(loanCurrency).getAmount();
+        boolean isLatestInstallment = Objects.equals(installment.getId(), 
latestInstallment.getId());
+        for (LoanTransaction loanTransaction : chargebackTransactions) {
+            boolean isLoanTransactionIsOnOrAfterInstallmentFromDate = 
DateUtils.isEqual(loanTransaction.getTransactionDate(),
+                    installment.getFromDate()) || 
DateUtils.isAfter(loanTransaction.getTransactionDate(), 
installment.getFromDate());
+            boolean isLoanTransactionIsBeforeNotLastInstallmentDueDate = 
!isLatestInstallment
+                    && 
DateUtils.isBefore(loanTransaction.getTransactionDate(), 
installment.getDueDate());
+            boolean isLoanTransactionIsOnOrBeforeLastInstallmentDueDate = 
isLatestInstallment
+                    && 
(DateUtils.isEqual(loanTransaction.getTransactionDate(), 
installment.getDueDate())
+                            || 
DateUtils.isBefore(loanTransaction.getTransactionDate(), 
installment.getDueDate()));
+            if (isLoanTransactionIsOnOrAfterInstallmentFromDate
+                    && (isLoanTransactionIsBeforeNotLastInstallmentDueDate || 
isLoanTransactionIsOnOrBeforeLastInstallmentDueDate)) {
+                amountAvailable = 
amountAvailable.subtract(loanTransaction.getAmount());
+                if (amountAvailable.compareTo(BigDecimal.ZERO) < 0) {
+                    overdueSinceDate = loanTransaction.getTransactionDate();
+                    break;
+                }
+            }
+        }
+        collectionData.setDelinquentDate(overdueSinceDate);
+        collectionData.setDelinquentAmount(outstandingAmount);
+        return collectionData;
+    }
+
+    private CollectionData 
calculateDelinquencyDataForNonOverdueInstallment(final Loan loan,
+            final LoanRepaymentScheduleInstallment installment) {
+        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        final MonetaryCurrency loanCurrency = loan.getCurrency();
+
+        LocalDate overdueSinceDate = null;
+        CollectionData collectionData = CollectionData.template();
+        BigDecimal outstandingAmount = BigDecimal.ZERO;
+
+        List<LoanTransaction> chargebackTransactions = 
loan.getLoanTransactions(LoanTransaction::isChargeback);
+        BigDecimal amountAvailable = 
installment.getTotalPaid(loanCurrency).getAmount();
+        for (LoanTransaction loanTransaction : chargebackTransactions) {
+
+            boolean isLoanTransactionIsOnOrAfterInstallmentFromDate = 
DateUtils.isEqual(loanTransaction.getTransactionDate(),
+                    installment.getFromDate()) || 
DateUtils.isAfter(loanTransaction.getTransactionDate(), 
installment.getFromDate());
+            boolean isLoanTransactionIsBeforeInstallmentDueDate = 
DateUtils.isBefore(loanTransaction.getTransactionDate(),
+                    installment.getDueDate());
+            boolean isLoanTransactionIsBeforeBusinessDate = 
DateUtils.isBefore(loanTransaction.getTransactionDate(), businessDate);
+            if (isLoanTransactionIsOnOrAfterInstallmentFromDate && 
isLoanTransactionIsBeforeInstallmentDueDate
+                    && isLoanTransactionIsBeforeBusinessDate) {
+                amountAvailable = 
amountAvailable.subtract(loanTransaction.getAmount());
+                if (amountAvailable.compareTo(BigDecimal.ZERO) < 0) {
+                    overdueSinceDate = loanTransaction.getTransactionDate();
+                }
+            }
+        }
+        if (amountAvailable.compareTo(BigDecimal.ZERO) < 0) {
+            outstandingAmount = outstandingAmount.add(amountAvailable.abs());
+        }
+        collectionData.setDelinquentDate(overdueSinceDate);
+        collectionData.setDelinquentAmount(outstandingAmount);
         return collectionData;
     }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
index 40131394b..88c27afa8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java
@@ -23,6 +23,7 @@ import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMapping
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
 import 
org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
 import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
 import 
org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper;
@@ -50,9 +51,11 @@ public class DelinquencyConfiguration {
             DelinquencyBucketRepository repositoryBucket, 
LoanDelinquencyTagHistoryRepository repositoryLoanDelinquencyTagHistory,
             DelinquencyRangeMapper mapperRange, DelinquencyBucketMapper 
mapperBucket,
             LoanDelinquencyTagMapper mapperLoanDelinquencyTagHistory, 
LoanRepository loanRepository,
-            LoanDelinquencyDomainService loanDelinquencyDomainService) {
+            LoanDelinquencyDomainService loanDelinquencyDomainService,
+            LoanInstallmentDelinquencyTagRepository 
repositoryLoanInstallmentDelinquencyTag) {
         return new DelinquencyReadPlatformServiceImpl(repositoryRange, 
repositoryBucket, repositoryLoanDelinquencyTagHistory, mapperRange,
-                mapperBucket, mapperLoanDelinquencyTagHistory, loanRepository, 
loanDelinquencyDomainService);
+                mapperBucket, mapperLoanDelinquencyTagHistory, loanRepository, 
loanDelinquencyDomainService,
+                repositoryLoanInstallmentDelinquencyTag);
     }
 
     @Bean
@@ -62,10 +65,11 @@ public class DelinquencyConfiguration {
             DelinquencyBucketRepository repositoryBucket, 
DelinquencyBucketMappingsRepository repositoryBucketMappings,
             LoanDelinquencyTagHistoryRepository loanDelinquencyTagRepository, 
LoanRepositoryWrapper loanRepository,
             LoanProductRepository loanProductRepository, 
BusinessEventNotifierService businessEventNotifierService,
-            LoanDelinquencyDomainService loanDelinquencyDomainService) {
+            LoanDelinquencyDomainService loanDelinquencyDomainService,
+            LoanInstallmentDelinquencyTagRepository 
loanInstallmentDelinquencyTagRepository) {
         return new DelinquencyWritePlatformServiceImpl(dataValidatorBucket, 
dataValidatorRange, repositoryRange, repositoryBucket,
                 repositoryBucketMappings, loanDelinquencyTagRepository, 
loanRepository, loanProductRepository, businessEventNotifierService,
-                loanDelinquencyDomainService);
+                loanDelinquencyDomainService, 
loanInstallmentDelinquencyTagRepository);
     }
 
     @Bean
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDelinquencyData.java
similarity index 62%
copy from 
fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDelinquencyData.java
index 5cd19cb5f..3efaea390 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDelinquencyData.java
@@ -16,19 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.delinquency.service;
+package org.apache.fineract.portfolio.loanaccount.data;
 
-import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
-import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import java.util.Map;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
 
-public interface LoanDelinquencyDomainService {
-
-    /**
-     * This method is to calculate the Overdue date and other properties, If 
the loan is overdue or If there is some
-     * Charge back transaction
-     *
-     * @param loan
-     */
-    CollectionData getOverdueCollectionData(Loan loan);
+@AllArgsConstructor
+@ToString
+@Getter
+@Setter
+public class LoanDelinquencyData {
 
+    private CollectionData loanCollectionData;
+    private Map<Long, CollectionData> loanInstallmentsCollectionData;
 }
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
index 866c87d4a..ab85206e2 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.portfolio.deliquency;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyIterable;
 import static org.mockito.Mockito.times;
@@ -30,6 +31,7 @@ import java.time.LocalDate;
 import java.time.ZoneId;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -46,13 +48,17 @@ import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketReposit
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyRange;
 import 
org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository;
 import 
org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTag;
+import 
org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository;
 import 
org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformServiceImpl;
 import 
org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService;
 import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
 import 
org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
 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.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
@@ -88,6 +94,9 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
     private BusinessEventNotifierService businessEventNotifierService;
     @Mock
     private LoanDelinquencyDomainService loanDelinquencyDomainService;
+    @Mock
+    private LoanInstallmentDelinquencyTagRepository 
loanInstallmentDelinquencyTagRepository;
+
     @InjectMocks
     private DelinquencyWritePlatformServiceImpl underTest;
 
@@ -120,11 +129,15 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
                 null, null);
 
+        Map<Long, CollectionData> installmentsCollection = new HashMap<>();
+
+        LoanDelinquencyData loanDelinquencyData = new 
LoanDelinquencyData(collectionData, installmentsCollection);
+
         when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
         when(loanProduct.getDelinquencyBucket()).thenReturn(delinquencyBucket);
         when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
         when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), 
any())).thenReturn(Optional.empty());
-        
when(loanDelinquencyDomainService.getOverdueCollectionData(loanForProcessing)).thenReturn(collectionData);
+        
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
 
         // when
         underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
@@ -155,11 +168,17 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
         LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 2L,
                 loanForProcessing);
 
+        CollectionData collectionData = CollectionData.template();
+
+        Map<Long, CollectionData> installmentsCollection = new HashMap<>();
+
+        LoanDelinquencyData loanDelinquencyData = new 
LoanDelinquencyData(collectionData, installmentsCollection);
+
         when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
         when(loanProduct.getDelinquencyBucket()).thenReturn(delinquencyBucket);
         when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
         when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), 
any())).thenReturn(Optional.empty());
-        
when(loanDelinquencyDomainService.getOverdueCollectionData(loanForProcessing)).thenReturn(CollectionData.template());
+        
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
 
         // when
         underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
@@ -189,4 +208,142 @@ public class 
DelinquencyWritePlatformServiceRangeChangeEventTest {
 
     }
 
+    @Test
+    public void 
givenLoanAccountWithOverdueInstallmentAndEnableInstallmentThenDelinquencyRangeIsSetForInstallmentTest()
 {
+        ArgumentCaptor<List<LoanInstallmentDelinquencyTag>> 
loanInstallmentDelinquencyTagsArgumentCaptor = ArgumentCaptor
+                .forClass(List.class);
+        // given
+        Loan loanForProcessing = Mockito.mock(Loan.class);
+        LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
+        DelinquencyRange range1 = DelinquencyRange.instance("Range1", 1, 2);
+        range1.setId(1L);
+        DelinquencyRange range2 = DelinquencyRange.instance("Range30", 3, 30);
+        range2.setId(2L);
+        List<DelinquencyRange> listDelinquencyRanges = Arrays.asList(range1, 
range2);
+        DelinquencyBucket delinquencyBucket = new DelinquencyBucket("test 
Bucket");
+        delinquencyBucket.setRanges(listDelinquencyRanges);
+
+        final Long daysDiff = 2L;
+        final LocalDate fromDate = 
DateUtils.getBusinessLocalDate().minusMonths(1).minusDays(daysDiff);
+        final LocalDate dueDate = 
DateUtils.getBusinessLocalDate().minusDays(daysDiff);
+        final BigDecimal installmentPrincipalAmount = BigDecimal.valueOf(100);
+        final BigDecimal zeroAmount = BigDecimal.ZERO;
+
+        LoanRepaymentScheduleInstallment installment = new 
LoanRepaymentScheduleInstallment(loanForProcessing, 1, fromDate, dueDate,
+                installmentPrincipalAmount, zeroAmount, zeroAmount, 
zeroAmount, false, new HashSet<>(), zeroAmount);
+        installment.setId(1L);
+
+        List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = 
Arrays.asList(installment);
+
+        LocalDate overDueSinceDate = 
DateUtils.getBusinessLocalDate().minusDays(2);
+        LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
+                loanForProcessing);
+        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null,
+                null, null);
+
+        CollectionData installmentCollectionData = new 
CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate,
+                installmentPrincipalAmount, null, null, null, null);
+
+        Map<Long, CollectionData> installmentsCollection = new HashMap<>();
+        installmentsCollection.put(1L, installmentCollectionData);
+
+        LoanDelinquencyData loanDelinquencyData = new 
LoanDelinquencyData(collectionData, installmentsCollection);
+
+        when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
+        when(loanProduct.getDelinquencyBucket()).thenReturn(delinquencyBucket);
+        when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
+        
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
+        when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), 
any())).thenReturn(Optional.empty());
+        
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
+        
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing,
 repaymentScheduleInstallments.get(0)))
+                .thenReturn(Optional.empty());
+
+        // when
+        underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
+
+        // then
+        verify(loanDelinquencyTagRepository, 
times(1)).saveAllAndFlush(anyIterable());
+        verify(loanInstallmentDelinquencyTagRepository, 
times(1)).saveAllAndFlush(loanInstallmentDelinquencyTagsArgumentCaptor.capture());
+
+        List<LoanInstallmentDelinquencyTag> installmentDelinquencyTags = 
loanInstallmentDelinquencyTagsArgumentCaptor.getValue();
+        assertEquals(1, installmentDelinquencyTags.size());
+        assertEquals(1, 
installmentDelinquencyTags.get(0).getInstallment().getInstallmentNumber());
+        assertEquals(1, 
installmentDelinquencyTags.get(0).getDelinquencyRange().getId());
+        assertEquals(installmentPrincipalAmount, 
installmentDelinquencyTags.get(0).getOutstandingAmount());
+    }
+
+    @Test
+    public void 
givenLoanAccountWithOverdueInstallmentAndEnableInstallmentThenDelinquencyRangeChangesForInstallmentTest()
 {
+        ArgumentCaptor<List<LoanInstallmentDelinquencyTag>> 
loanInstallmentDelinquencyTagsArgumentCaptor = ArgumentCaptor
+                .forClass(List.class);
+        ArgumentCaptor<LoanInstallmentDelinquencyTag> 
loanInstallmentDelinquencyTagArgumentCaptorForDelete = ArgumentCaptor
+                .forClass(LoanInstallmentDelinquencyTag.class);
+        // given
+        Loan loanForProcessing = Mockito.mock(Loan.class);
+        LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
+        DelinquencyRange range1 = DelinquencyRange.instance("Range1", 1, 2);
+        range1.setId(1L);
+        DelinquencyRange range2 = DelinquencyRange.instance("Range30", 3, 30);
+        range2.setId(2L);
+        List<DelinquencyRange> listDelinquencyRanges = Arrays.asList(range1, 
range2);
+        DelinquencyBucket delinquencyBucket = new DelinquencyBucket("test 
Bucket");
+        delinquencyBucket.setRanges(listDelinquencyRanges);
+
+        final Long daysDiff = 2L;
+        final LocalDate fromDate = 
DateUtils.getBusinessLocalDate().minusMonths(1).minusDays(daysDiff);
+        final LocalDate dueDate = 
DateUtils.getBusinessLocalDate().minusDays(daysDiff);
+        final BigDecimal installmentPrincipalAmount = BigDecimal.valueOf(100);
+        final BigDecimal zeroAmount = BigDecimal.ZERO;
+
+        LoanRepaymentScheduleInstallment installment = new 
LoanRepaymentScheduleInstallment(loanForProcessing, 1, fromDate, dueDate,
+                installmentPrincipalAmount, zeroAmount, zeroAmount, 
zeroAmount, false, new HashSet<>(), zeroAmount);
+        installment.setId(1L);
+
+        List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = 
Arrays.asList(installment);
+
+        LocalDate overDueSinceDate = 
DateUtils.getBusinessLocalDate().minusDays(29);
+        LoanScheduleDelinquencyData loanScheduleDelinquencyData = new 
LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
+                loanForProcessing);
+        CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 
29L, null, 29L, overDueSinceDate, BigDecimal.ZERO, null, null,
+                null, null);
+
+        CollectionData installmentCollectionData = new 
CollectionData(BigDecimal.ZERO, 29L, null, 29L, overDueSinceDate,
+                installmentPrincipalAmount, null, null, null, null);
+
+        Map<Long, CollectionData> installmentsCollection = new HashMap<>();
+        installmentsCollection.put(1L, installmentCollectionData);
+
+        LoanDelinquencyData loanDelinquencyData = new 
LoanDelinquencyData(collectionData, installmentsCollection);
+
+        LoanInstallmentDelinquencyTag previousInstallmentTag = new 
LoanInstallmentDelinquencyTag();
+        previousInstallmentTag.setDelinquencyRange(range1);
+
+        when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
+        when(loanProduct.getDelinquencyBucket()).thenReturn(delinquencyBucket);
+        when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
+        
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
+        when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), 
any())).thenReturn(Optional.empty());
+        
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
+        
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing,
 repaymentScheduleInstallments.get(0)))
+                .thenReturn(Optional.of(previousInstallmentTag));
+
+        // when
+        underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
+
+        // then
+        verify(loanDelinquencyTagRepository, 
times(1)).saveAllAndFlush(anyIterable());
+        verify(loanInstallmentDelinquencyTagRepository, 
times(1)).saveAllAndFlush(loanInstallmentDelinquencyTagsArgumentCaptor.capture());
+        verify(loanInstallmentDelinquencyTagRepository, 
times(1)).delete(loanInstallmentDelinquencyTagArgumentCaptorForDelete.capture());
+
+        List<LoanInstallmentDelinquencyTag> installmentDelinquencyTags = 
loanInstallmentDelinquencyTagsArgumentCaptor.getValue();
+        assertEquals(1, installmentDelinquencyTags.size());
+        assertEquals(1, 
installmentDelinquencyTags.get(0).getInstallment().getInstallmentNumber());
+        assertEquals(2, 
installmentDelinquencyTags.get(0).getDelinquencyRange().getId());
+        assertEquals(installmentPrincipalAmount, 
installmentDelinquencyTags.get(0).getOutstandingAmount());
+
+        LoanInstallmentDelinquencyTag deletedInstallmentDelinquencyTag = 
loanInstallmentDelinquencyTagArgumentCaptorForDelete.getValue();
+        assertNotNull(deletedInstallmentDelinquencyTag);
+        assertEquals(previousInstallmentTag, deletedInstallmentDelinquencyTag);
+
+    }
 }
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
index c75580724..047f7a08d 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.portfolio.deliquency;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.mockito.Mockito.when;
 
 import java.math.BigDecimal;
@@ -43,6 +44,7 @@ import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
 import 
org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainServiceImpl;
 import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -185,4 +187,95 @@ public class LoanDelinquencyDomainServiceTest {
 
     }
 
+    @Test
+    public void 
givenLoanInstallmentWithOverdueEnableInstallmentDelinquencyThenCalculateDelinquentData()
 {
+        // given
+        final Long daysDiff = 2L;
+        final LocalDate fromDate = 
businessDate.minusMonths(1).minusDays(daysDiff);
+        final LocalDate dueDate = businessDate.minusDays(daysDiff);
+
+        LoanRepaymentScheduleInstallment installment = new 
LoanRepaymentScheduleInstallment(loan, 1, fromDate, dueDate, principal,
+                zeroAmount, zeroAmount, zeroAmount, false, new HashSet<>(), 
zeroAmount);
+        installment.setId(1L);
+        List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = 
Arrays.asList(installment);
+
+        // when
+        when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
+        
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
+        
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
+        
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Collections.emptyList());
+        
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
+        when(loan.getCurrency()).thenReturn(currency);
+        when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
+
+        LoanDelinquencyData collectionData = 
underTest.getLoanDelinquencyData(loan);
+
+        // then
+        assertNotNull(collectionData);
+        assertNotNull(collectionData.getLoanInstallmentsCollectionData());
+        assertEquals(1L, 
collectionData.getLoanInstallmentsCollectionData().size());
+
+        CollectionData loanCollectionData = 
collectionData.getLoanCollectionData();
+        CollectionData installmentCollectionData = 
collectionData.getLoanInstallmentsCollectionData().get(1L);
+
+        assertEquals(daysDiff, loanCollectionData.getDelinquentDays());
+        assertEquals(dueDate, loanCollectionData.getDelinquentDate());
+        assertEquals(loanCollectionData.getDelinquentDays(), 
loanCollectionData.getPastDueDays());
+
+        assertEquals(daysDiff, installmentCollectionData.getDelinquentDays());
+        assertEquals(dueDate, installmentCollectionData.getDelinquentDate());
+        assertEquals(installmentCollectionData.getDelinquentDays(), 
installmentCollectionData.getPastDueDays());
+
+    }
+
+    @Test
+    public void 
givenLoanInstallmentWithoutOverdueWithChargebackAndEnableInstallmentDelinquencyThenCalculateDelinquentData()
 {
+
+        // given
+        PaymentDetail paymentDetail = Mockito.mock(PaymentDetail.class);
+        Long daysDiff = 2L;
+        final LocalDate fromDate = 
businessDate.minusMonths(1).plusDays(daysDiff);
+        final LocalDate dueDate = businessDate.plusDays(daysDiff);
+        final LocalDate transactionDate = businessDate.minusDays(daysDiff);
+
+        final Money zeroMoney = Money.zero(currency);
+        LoanRepaymentScheduleInstallment installment = new 
LoanRepaymentScheduleInstallment(loan, 1, fromDate, dueDate, principal,
+                zeroAmount, zeroAmount, zeroAmount, false, new HashSet<>(), 
zeroAmount);
+        installment.setId(1L);
+        LoanTransaction loanTransaction = LoanTransaction.chargeback(loan, 
Money.of(currency, principal), paymentDetail, transactionDate,
+                null);
+        
installment.getLoanTransactionToRepaymentScheduleMappings().add(LoanTransactionToRepaymentScheduleMapping
+                .createFrom(loanTransaction, installment, zeroMoney, 
zeroMoney, zeroMoney, zeroMoney));
+
+        List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = 
Arrays.asList(installment);
+        // when
+        when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
+        
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
+        
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
+        when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
+        when(loan.getCurrency()).thenReturn(currency);
+        
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Arrays.asList(loanTransaction));
+
+        LoanDelinquencyData collectionData = 
underTest.getLoanDelinquencyData(loan);
+
+        // then
+        assertNotNull(collectionData);
+        assertNotNull(collectionData.getLoanInstallmentsCollectionData());
+        assertEquals(1L, 
collectionData.getLoanInstallmentsCollectionData().size());
+
+        CollectionData loanCollectionData = 
collectionData.getLoanCollectionData();
+        CollectionData installmentCollectionData = 
collectionData.getLoanInstallmentsCollectionData().get(1L);
+
+        assertEquals(daysDiff, loanCollectionData.getDelinquentDays());
+        assertEquals(transactionDate, loanCollectionData.getDelinquentDate());
+        assertEquals(loanCollectionData.getDelinquentDays(), 
loanCollectionData.getPastDueDays());
+
+        // then
+        assertEquals(daysDiff, installmentCollectionData.getDelinquentDays());
+        assertEquals(transactionDate, 
installmentCollectionData.getDelinquentDate());
+        assertEquals(installmentCollectionData.getDelinquentDays(), 
installmentCollectionData.getPastDueDays());
+        assertEquals(0, 
principal.compareTo(installmentCollectionData.getDelinquentAmount()));
+
+    }
+
 }

Reply via email to