From 6c72ee49d8d9fe8c8d5ead8acac4445b0c83bb65 Mon Sep 17 00:00:00 2001 From: Ruchi Dhamankar Date: Wed, 2 Nov 2022 21:26:56 +0530 Subject: [PATCH] FINERACT-1734-Repayment-due-event --- .../CheckLoanRepaymentDueBusinessStep.java | 63 ++++++++++ ...CheckLoanRepaymentOverdueBusinessStep.java | 66 +++++++++++ .../domain/ConfigurationDomainService.java | 4 + .../domain/ConfigurationDomainServiceJpa.java | 14 +++ .../loan/LoanRepaymentDueBusinessEvent.java | 35 ++++++ .../LoanRepaymentOverdueBusinessEvent.java | 35 ++++++ .../db/changelog/tenant/changelog-tenant.xml | 1 + ...tions_for_repayment_due_business_steps.xml | 68 +++++++++++ ...CheckLoanRepaymentDueBusinessStepTest.java | 111 ++++++++++++++++++ ...kLoanRepaymentOverdueBusinessStepTest.java | 110 +++++++++++++++++ ...entConfigurationValidationServiceTest.java | 10 +- .../ExternalEventConfigurationHelper.java | 10 ++ .../common/GlobalConfigurationHelper.java | 20 +++- 13 files changed, 541 insertions(+), 6 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentDueBusinessEvent.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanRepaymentOverdueBusinessEvent.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0067_add_configurations_for_repayment_due_business_steps.xml create mode 100644 fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java 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 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 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 @@ + 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 @@ + + + + + + SELECT SETVAL('c_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM c_configuration; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 loanRepaymentDueEvent = ArgumentCaptor.forClass(LoanRepaymentDueBusinessEvent.class); + // given + when(configurationDomainService.retrieveRepaymentDueDays()).thenReturn(1L); + LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1); + Loan loanForProcessing = Mockito.mock(Loan.class); + List 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 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 loanRepaymentOverdueBusinessEventArgumentCaptor = ArgumentCaptor + .forClass(LoanRepaymentOverdueBusinessEvent.class); + // given + when(configurationDomainService.retrieveRepaymentOverdueDays()).thenReturn(1L); + LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().minusDays(1); + Collection 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 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 loanRepaymentDueBusinessEvent = new HashMap<>(); + loanRepaymentDueBusinessEvent.put("type", "LoanRepaymentDueBusinessEvent"); + loanRepaymentDueBusinessEvent.put("enabled", false); + defaults.add(loanRepaymentDueBusinessEvent); + + Map loanRepaymentOverdueBusinessEvent = new HashMap<>(); + loanRepaymentOverdueBusinessEvent.put("type", "LoanRepaymentOverdueBusinessEvent"); + loanRepaymentOverdueBusinessEvent.put("enabled", false); + defaults.add(loanRepaymentOverdueBusinessEvent); + Map 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 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 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 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; }