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/03 10:50:54 UTC

[fineract] branch develop updated: FINERACT-1734-Repayment-due-event

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 6c72ee49d  FINERACT-1734-Repayment-due-event
6c72ee49d is described below

commit 6c72ee49d8d9fe8c8d5ead8acac4445b0c83bb65
Author: Ruchi Dhamankar <ru...@gmail.com>
AuthorDate: Wed Nov 2 21:26:56 2022 +0530

     FINERACT-1734-Repayment-due-event
---
 .../loan/CheckLoanRepaymentDueBusinessStep.java    |  63 ++++++++++++
 .../CheckLoanRepaymentOverdueBusinessStep.java     |  66 ++++++++++++
 .../domain/ConfigurationDomainService.java         |   4 +
 .../domain/ConfigurationDomainServiceJpa.java      |  14 +++
 .../domain/loan/LoanRepaymentDueBusinessEvent.java |  35 +++++++
 .../loan/LoanRepaymentOverdueBusinessEvent.java    |  35 +++++++
 .../db/changelog/tenant/changelog-tenant.xml       |   1 +
 ...igurations_for_repayment_due_business_steps.xml |  68 +++++++++++++
 .../CheckLoanRepaymentDueBusinessStepTest.java     | 111 +++++++++++++++++++++
 .../CheckLoanRepaymentOverdueBusinessStepTest.java | 110 ++++++++++++++++++++
 ...nalEventConfigurationValidationServiceTest.java |  10 +-
 .../common/ExternalEventConfigurationHelper.java   |  10 ++
 .../common/GlobalConfigurationHelper.java          |  20 +++-
 13 files changed, 541 insertions(+), 6 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java
new file mode 100644
index 000000000..a85bf6b38
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java
@@ -0,0 +1,63 @@
+/**
+ * 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.cob.loan;
+
+import java.time.LocalDate;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanRepaymentDueBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class CheckLoanRepaymentDueBusinessStep implements LoanCOBBusinessStep {
+
+    private final ConfigurationDomainService configurationDomainService;
+    private final BusinessEventNotifierService businessEventNotifierService;
+
+    @Override
+    public Loan execute(Loan loan) {
+        Long numberOfDaysBeforeDueDateToRaiseEvent = configurationDomainService.retrieveRepaymentDueDays();
+        final LocalDate currentDate = DateUtils.getBusinessLocalDate();
+        final List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
+        for (LoanRepaymentScheduleInstallment repaymentSchedule : loanRepaymentScheduleInstallments) {
+            LocalDate repaymentDate = repaymentSchedule.getDueDate();
+            if (repaymentDate.minusDays(numberOfDaysBeforeDueDateToRaiseEvent).equals(currentDate)) {
+                businessEventNotifierService.notifyPostBusinessEvent(new LoanRepaymentDueBusinessEvent(loan));
+                break;
+            }
+        }
+        return loan;
+    }
+
+    @Override
+    public String getEnumStyledName() {
+        return "CHECK_LOAN_REPAYMENT_DUE";
+    }
+
+    @Override
+    public String getHumanReadableName() {
+        return "Check loan repayment due";
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java
new file mode 100644
index 000000000..fe56c47d6
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java
@@ -0,0 +1,66 @@
+/**
+ * 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.cob.loan;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanRepaymentOverdueBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
+import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class CheckLoanRepaymentOverdueBusinessStep implements LoanCOBBusinessStep {
+
+    private final LoanReadPlatformService loanReadPlatformService;
+    private final ConfigurationDomainService configurationDomainService;
+    private final BusinessEventNotifierService businessEventNotifierService;
+
+    @Override
+    public Loan execute(Loan loan) {
+        Long numberOfDaysAfterDueDateToRaiseEvent = configurationDomainService.retrieveRepaymentOverdueDays();
+        final LocalDate currentDate = DateUtils.getBusinessLocalDate();
+        final Collection<OverdueLoanScheduleData> overdueInstallmentsForLoan = loanReadPlatformService
+                .retrieveAllOverdueInstallmentsForLoan(loan);
+        for (OverdueLoanScheduleData overdueInstallment : overdueInstallmentsForLoan) {
+            LocalDate installmentDueDate = LocalDate.parse(overdueInstallment.getDueDate());
+            if (installmentDueDate.plusDays(numberOfDaysAfterDueDateToRaiseEvent).equals(currentDate)) {
+                businessEventNotifierService.notifyPostBusinessEvent(new LoanRepaymentOverdueBusinessEvent(loan));
+                break;
+            }
+        }
+        return loan;
+    }
+
+    @Override
+    public String getEnumStyledName() {
+        return "CHECK_LOAN_REPAYMENT_OVERDUE";
+    }
+
+    @Override
+    public String getHumanReadableName() {
+        return "Check loan repayment overdue";
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
index 9d7d23d2a..6672d07af 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
@@ -120,4 +120,8 @@ public interface ConfigurationDomainService {
     boolean isReversalTransactionAllowed();
 
     Long retrieveExternalEventsPurgeDaysCriteria();
+
+    Long retrieveRepaymentDueDays();
+
+    Long retrieveRepaymentOverdueDays();
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
index e684eb80a..68c6c539e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
@@ -41,6 +41,8 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
     public static final String ENABLE_BUSINESS_DATE = "enable_business_date";
     public static final String ENABLE_AUTOMATIC_COB_DATE_ADJUSTMENT = "enable_automatic_cob_date_adjustment";
     public static final String EXTERNAL_EVENTS_PURGE_DAYS = "purge-external-events-older-than-days";
+    private static final String DAYS_BEFORE_REPAYMENT_IS_DUE = "days-before-repayment-is-due";
+    private static final String DAYS_AFTER_REPAYMENT_IS_OVERDUE = "days-after-repayment-is-overdue";
     private final PermissionRepository permissionRepository;
     private final GlobalConfigurationRepositoryWrapper globalConfigurationRepository;
     private final PlatformCacheRepository cacheTypeRepository;
@@ -459,4 +461,16 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
         return property.getValue();
 
     }
+
+    @Override
+    public Long retrieveRepaymentDueDays() {
+        final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(DAYS_BEFORE_REPAYMENT_IS_DUE);
+        return property.getValue();
+    }
+
+    @Override
+    public Long retrieveRepaymentOverdueDays() {
+        final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(DAYS_AFTER_REPAYMENT_IS_OVERDUE);
+        return property.getValue();
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentDueBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentDueBusinessEvent.java
new file mode 100644
index 000000000..3cb3fb491
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentDueBusinessEvent.java
@@ -0,0 +1,35 @@
+/**
+ * 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.infrastructure.event.business.domain.loan;
+
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+public class LoanRepaymentDueBusinessEvent extends LoanBusinessEvent {
+
+    private static final String TYPE = "LoanRepaymentDueBusinessEvent";
+
+    public LoanRepaymentDueBusinessEvent(Loan value) {
+        super(value);
+    }
+
+    @Override
+    public String getType() {
+        return TYPE;
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentOverdueBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentOverdueBusinessEvent.java
new file mode 100644
index 000000000..f6f4053be
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentOverdueBusinessEvent.java
@@ -0,0 +1,35 @@
+/**
+ * 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.infrastructure.event.business.domain.loan;
+
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+
+public class LoanRepaymentOverdueBusinessEvent extends LoanBusinessEvent {
+
+    private static final String TYPE = "LoanRepaymentOverdueBusinessEvent";
+
+    public LoanRepaymentOverdueBusinessEvent(Loan value) {
+        super(value);
+    }
+
+    @Override
+    public String getType() {
+        return TYPE;
+    }
+}
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 e2750a085..886cd6d04 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
@@ -86,4 +86,5 @@
     <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"/>
+    <include file="parts/0067_add_configurations_for_repayment_due_business_steps.xml" relativeToChangelogFile="true"/>
 </databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0067_add_configurations_for_repayment_due_business_steps.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0067_add_configurations_for_repayment_due_business_steps.xml
new file mode 100644
index 000000000..21e456af8
--- /dev/null
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0067_add_configurations_for_repayment_due_business_steps.xml
@@ -0,0 +1,68 @@
+<?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.3.xsd">
+    <changeSet author="fineract" id="1" context="postgresql">
+        <sql>
+            SELECT SETVAL('c_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM c_configuration;
+        </sql>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <insert tableName="c_configuration">
+            <column name="name" value="days-before-repayment-is-due"/>
+            <column name="value" valueNumeric="1"/>
+            <column name="date_value"/>
+            <column name="string_value"/>
+            <column name="enabled" valueBoolean="false"/>
+            <column name="is_trap_door" valueBoolean="false"/>
+            <column name="description" value="Number of days before repayment is due to raise event"/>
+        </insert>
+        <insert tableName="c_configuration">
+            <column name="name" value="days-after-repayment-is-overdue"/>
+            <column name="value" valueNumeric="1"/>
+            <column name="date_value"/>
+            <column name="string_value"/>
+            <column name="enabled" valueBoolean="false"/>
+            <column name="is_trap_door" valueBoolean="false"/>
+            <column name="description" value="Number of days after repayment overdue to raise event"/>
+        </insert>
+        <insert tableName="m_external_event_configuration">
+            <column name="type" value="LoanRepaymentDueBusinessEvent"/>
+            <column name="enabled" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_external_event_configuration">
+            <column name="type" value="LoanRepaymentOverdueBusinessEvent"/>
+            <column name="enabled" valueBoolean="false"/>
+        </insert>
+        <insert tableName="m_batch_business_steps">
+            <column name="job_name" value="LOAN_CLOSE_OF_BUSINESS"/>
+            <column name="step_name" value="CHECK_LOAN_REPAYMENT_DUE"/>
+            <column name="step_order" value="3"/>
+        </insert>
+        <insert tableName="m_batch_business_steps">
+            <column name="job_name" value="LOAN_CLOSE_OF_BUSINESS"/>
+            <column name="step_name" value="CHECK_LOAN_REPAYMENT_OVERDUE"/>
+            <column name="step_order" value="4"/>
+        </insert>
+    </changeSet>
+</databaseChangeLog>
diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java
new file mode 100644
index 000000000..2d412a275
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java
@@ -0,0 +1,111 @@
+/**
+ * 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.cob.loan;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.math.BigDecimal;
+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 org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanRepaymentDueBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class CheckLoanRepaymentDueBusinessStepTest {
+
+    @Mock
+    private ConfigurationDomainService configurationDomainService;
+    @Mock
+    private BusinessEventNotifierService businessEventNotifierService;
+    private CheckLoanRepaymentDueBusinessStep underTest;
+
+    @BeforeEach
+    public void setUp() {
+        ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
+        ThreadLocalContextUtil
+                .setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
+        underTest = new CheckLoanRepaymentDueBusinessStep(configurationDomainService, businessEventNotifierService);
+    }
+
+    @Test
+    public void givenLoanWithInstallmentDueAfterConfiguredDaysWhenStepExecutionThenBusinessEventIsRaised() {
+        ArgumentCaptor<LoanRepaymentDueBusinessEvent> loanRepaymentDueEvent = ArgumentCaptor.forClass(LoanRepaymentDueBusinessEvent.class);
+        // given
+        when(configurationDomainService.retrieveRepaymentDueDays()).thenReturn(1L);
+        LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1);
+        Loan loanForProcessing = Mockito.mock(Loan.class);
+        List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays
+                .asList(new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()),
+                        loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
+                        BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
+        when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
+
+        // when
+        Loan processedLoan = underTest.execute(loanForProcessing);
+        // then
+        verify(businessEventNotifierService, times(1)).notifyPostBusinessEvent(loanRepaymentDueEvent.capture());
+        Loan loanPayloadForEvent = loanRepaymentDueEvent.getValue().get();
+        assertEquals(loanForProcessing, loanPayloadForEvent);
+        assertEquals(processedLoan, loanPayloadForEvent);
+
+    }
+
+    @Test
+    public void givenLoanWithNoInstallmentDueAfterConfiguredDaysWhenStepExecutionThenNoBusinessEventIsRaised() {
+        // given
+        when(configurationDomainService.retrieveRepaymentDueDays()).thenReturn(1L);
+        LocalDate loanInstallmentRepaymentDueDateAfter5Days = DateUtils.getBusinessLocalDate().plusDays(5);
+        Loan loanForProcessing = Mockito.mock(Loan.class);
+        List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays
+                .asList(new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()),
+                        loanInstallmentRepaymentDueDateAfter5Days, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
+                        BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
+        when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
+
+        // when
+        Loan processedLoan = underTest.execute(loanForProcessing);
+        // then
+        verify(businessEventNotifierService, times(0)).notifyPostBusinessEvent(any());
+        assertEquals(processedLoan, loanForProcessing);
+
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java
new file mode 100644
index 000000000..6b34207e9
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java
@@ -0,0 +1,110 @@
+/**
+ * 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.cob.loan;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanRepaymentOverdueBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
+import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+public class CheckLoanRepaymentOverdueBusinessStepTest {
+
+    @Mock
+    private LoanReadPlatformService loanReadPlatformService;
+    @Mock
+    private ConfigurationDomainService configurationDomainService;
+    @Mock
+    private BusinessEventNotifierService businessEventNotifierService;
+    private CheckLoanRepaymentOverdueBusinessStep underTest;
+
+    @BeforeEach
+    public void setUp() {
+        ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
+        ThreadLocalContextUtil
+                .setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
+        underTest = new CheckLoanRepaymentOverdueBusinessStep(loanReadPlatformService, configurationDomainService,
+                businessEventNotifierService);
+    }
+
+    @Test
+    public void givenLoanWithInstallmentOverdueAfterConfiguredDaysWhenStepExecutionThenBusinessEventIsRaised() {
+        ArgumentCaptor<LoanRepaymentOverdueBusinessEvent> loanRepaymentOverdueBusinessEventArgumentCaptor = ArgumentCaptor
+                .forClass(LoanRepaymentOverdueBusinessEvent.class);
+        // given
+        when(configurationDomainService.retrieveRepaymentOverdueDays()).thenReturn(1L);
+        LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().minusDays(1);
+        Collection<OverdueLoanScheduleData> overdueInstallmentsForLoan = Arrays
+                .asList(new OverdueLoanScheduleData(1L, 1L, loanInstallmentRepaymentDueDate.toString(), BigDecimal.valueOf(0.0), "", "",
+                        BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), 1));
+        when(loanReadPlatformService.retrieveAllOverdueInstallmentsForLoan(any())).thenReturn(overdueInstallmentsForLoan);
+        Loan loanForProcessing = Mockito.mock(Loan.class);
+        // when
+        Loan processedLoan = underTest.execute(loanForProcessing);
+        // then
+        verify(businessEventNotifierService, times(1)).notifyPostBusinessEvent(loanRepaymentOverdueBusinessEventArgumentCaptor.capture());
+        Loan loanPayloadForEvent = loanRepaymentOverdueBusinessEventArgumentCaptor.getValue().get();
+        assertEquals(loanForProcessing, loanPayloadForEvent);
+        assertEquals(processedLoan, loanPayloadForEvent);
+    }
+
+    @Test
+    public void givenLoanWithNoInstallmentOverdueAfterConfiguredDaysWhenStepExecutionThenNoBusinessEventIsRaised() {
+        // given
+        when(configurationDomainService.retrieveRepaymentOverdueDays()).thenReturn(1L);
+        LocalDate loanInstallmentRepaymentDueDateBefore5Days = DateUtils.getBusinessLocalDate().minusDays(5);
+        Collection<OverdueLoanScheduleData> overdueInstallmentsForLoan = Arrays
+                .asList(new OverdueLoanScheduleData(1L, 1L, loanInstallmentRepaymentDueDateBefore5Days.toString(), BigDecimal.valueOf(0.0),
+                        "", "", BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), 1));
+        when(loanReadPlatformService.retrieveAllOverdueInstallmentsForLoan(any())).thenReturn(overdueInstallmentsForLoan);
+        Loan loanForProcessing = Mockito.mock(Loan.class);
+        // when
+        Loan processedLoan = underTest.execute(loanForProcessing);
+        // then
+        verify(businessEventNotifierService, times(0)).notifyPostBusinessEvent(any());
+        assertEquals(processedLoan, loanForProcessing);
+
+    }
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java
index f9bb17f03..85b98499f 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java
@@ -73,8 +73,9 @@ public class ExternalEventConfigurationValidationServiceTest {
                 "LoanInitiateTransferBusinessEvent", "LoanInterestRecalculationBusinessEvent", "LoanProductCreateBusinessEvent",
                 "LoanReassignOfficerBusinessEvent", "LoanRefundPostBusinessEvent", "LoanRefundPreBusinessEvent",
                 "LoanRejectedBusinessEvent", "LoanRejectTransferBusinessEvent", "LoanRemoveOfficerBusinessEvent",
-                "LoanRescheduledDueCalendarChangeBusinessEvent", "LoanRescheduledDueHolidayBusinessEvent",
-                "LoanScheduleVariationsAddedBusinessEvent", "LoanScheduleVariationsDeletedBusinessEvent", "LoanStatusChangedBusinessEvent",
+                "LoanRepaymentDueBusinessEvent", "LoanRepaymentOverdueBusinessEvent", "LoanRescheduledDueCalendarChangeBusinessEvent",
+                "LoanRescheduledDueHolidayBusinessEvent", "LoanScheduleVariationsAddedBusinessEvent",
+                "LoanScheduleVariationsDeletedBusinessEvent", "LoanStatusChangedBusinessEvent",
                 "LoanTransactionGoodwillCreditPostBusinessEvent", "LoanTransactionGoodwillCreditPreBusinessEvent",
                 "LoanTransactionMakeRepaymentPostBusinessEvent", "LoanTransactionMakeRepaymentPreBusinessEvent",
                 "LoanTransactionMerchantIssuedRefundPostBusinessEvent", "LoanTransactionMerchantIssuedRefundPreBusinessEvent",
@@ -144,8 +145,9 @@ public class ExternalEventConfigurationValidationServiceTest {
                 "LoanInitiateTransferBusinessEvent", "LoanInterestRecalculationBusinessEvent", "LoanProductCreateBusinessEvent",
                 "LoanReassignOfficerBusinessEvent", "LoanRefundPostBusinessEvent", "LoanRefundPreBusinessEvent",
                 "LoanRejectedBusinessEvent", "LoanRejectTransferBusinessEvent", "LoanRemoveOfficerBusinessEvent",
-                "LoanRescheduledDueCalendarChangeBusinessEvent", "LoanRescheduledDueHolidayBusinessEvent",
-                "LoanScheduleVariationsAddedBusinessEvent", "LoanScheduleVariationsDeletedBusinessEvent", "LoanStatusChangedBusinessEvent",
+                "LoanRepaymentDueBusinessEvent", "LoanRepaymentOverdueBusinessEvent", "LoanRescheduledDueCalendarChangeBusinessEvent",
+                "LoanRescheduledDueHolidayBusinessEvent", "LoanScheduleVariationsAddedBusinessEvent",
+                "LoanScheduleVariationsDeletedBusinessEvent", "LoanStatusChangedBusinessEvent",
                 "LoanTransactionGoodwillCreditPostBusinessEvent", "LoanTransactionGoodwillCreditPreBusinessEvent",
                 "LoanTransactionMakeRepaymentPostBusinessEvent", "LoanTransactionMakeRepaymentPreBusinessEvent",
                 "LoanTransactionMerchantIssuedRefundPostBusinessEvent", "LoanTransactionMerchantIssuedRefundPreBusinessEvent",
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java
index 385a44329..b3dbcf9d0 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java
@@ -220,6 +220,16 @@ public class ExternalEventConfigurationHelper {
         loanRemoveOfficerBusinessEvent.put("enabled", false);
         defaults.add(loanRemoveOfficerBusinessEvent);
 
+        Map<String, Object> loanRepaymentDueBusinessEvent = new HashMap<>();
+        loanRepaymentDueBusinessEvent.put("type", "LoanRepaymentDueBusinessEvent");
+        loanRepaymentDueBusinessEvent.put("enabled", false);
+        defaults.add(loanRepaymentDueBusinessEvent);
+
+        Map<String, Object> loanRepaymentOverdueBusinessEvent = new HashMap<>();
+        loanRepaymentOverdueBusinessEvent.put("type", "LoanRepaymentOverdueBusinessEvent");
+        loanRepaymentOverdueBusinessEvent.put("enabled", false);
+        defaults.add(loanRepaymentOverdueBusinessEvent);
+
         Map<String, Object> loanRescheduledDueCalendarChangeBusinessEvent = new HashMap<>();
         loanRescheduledDueCalendarChangeBusinessEvent.put("type", "LoanRescheduledDueCalendarChangeBusinessEvent");
         loanRescheduledDueCalendarChangeBusinessEvent.put("enabled", false);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
index 6154631b9..c110b3dac 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
@@ -99,8 +99,8 @@ public class GlobalConfigurationHelper {
         ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
 
         // There are currently 37 global configurations.
-        Assertions.assertEquals(42, expectedGlobalConfigurations.size());
-        Assertions.assertEquals(42, actualGlobalConfigurations.size());
+        Assertions.assertEquals(44, expectedGlobalConfigurations.size());
+        Assertions.assertEquals(44, actualGlobalConfigurations.size());
 
         for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
 
@@ -472,6 +472,22 @@ public class GlobalConfigurationHelper {
         purgeExternalEventsOlderThanDaysDefault.put("trapDoor", false);
         defaults.add(purgeExternalEventsOlderThanDaysDefault);
 
+        HashMap<String, Object> loanRepaymentDueDaysDefault = new HashMap<>();
+        loanRepaymentDueDaysDefault.put("id", 48);
+        loanRepaymentDueDaysDefault.put("name", "days-before-repayment-is-due");
+        loanRepaymentDueDaysDefault.put("value", 1);
+        loanRepaymentDueDaysDefault.put("enabled", false);
+        loanRepaymentDueDaysDefault.put("trapDoor", false);
+        defaults.add(loanRepaymentDueDaysDefault);
+
+        HashMap<String, Object> loanRepaymentOverdueDaysDefault = new HashMap<>();
+        loanRepaymentOverdueDaysDefault.put("id", 49);
+        loanRepaymentOverdueDaysDefault.put("name", "days-after-repayment-is-overdue");
+        loanRepaymentOverdueDaysDefault.put("value", 1);
+        loanRepaymentOverdueDaysDefault.put("enabled", false);
+        loanRepaymentOverdueDaysDefault.put("trapDoor", false);
+        defaults.add(loanRepaymentOverdueDaysDefault);
+
         return defaults;
     }