FINERACT-1992: Backdated delinquency pause events
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java
index fcf9d40..22a606c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java
@@ -157,12 +157,13 @@
.build();
// get list of charges for installments in same range
- List<LoanCharge> chargesForInstallmentsInSameRange = loan.getLoanCharges().stream()
- .filter(loanCharge -> !loanCharge.isPaid() && delinquentInstallmentsInSameRange.stream().anyMatch(
- installmentForCharge -> (DateUtils.isAfter(loanCharge.getDueDate(), installmentForCharge.getFromDate())
- || DateUtils.isEqual(loanCharge.getDueDate(), installmentForCharge.getFromDate()))
- && (DateUtils.isBefore(loanCharge.getDueDate(), installmentForCharge.getDueDate())
- || DateUtils.isEqual(loanCharge.getDueDate(), installmentForCharge.getDueDate()))))
+ List<LoanCharge> chargesForInstallmentsInSameRange = loan.getLoanCharges().stream().filter(loanCharge -> !loanCharge
+ .isPaid()
+ && delinquentInstallmentsInSameRange.stream().anyMatch(installmentForCharge -> (DateUtils
+ .isAfter(loanCharge.getEffectiveDueDate(), installmentForCharge.getFromDate())
+ || DateUtils.isEqual(loanCharge.getEffectiveDueDate(), installmentForCharge.getFromDate()))
+ && (DateUtils.isBefore(loanCharge.getEffectiveDueDate(), installmentForCharge.getDueDate())
+ || DateUtils.isEqual(loanCharge.getEffectiveDueDate(), installmentForCharge.getDueDate()))))
.toList();
List<LoanChargeDataRangeViewV1> charges = new ArrayList<>();
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 23a0f0c..2040299 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
@@ -236,6 +236,10 @@
if (DateUtils.isBefore(parsedDelinquencyAction.getStartDate(), businessDate)
&& DelinquencyAction.PAUSE.equals(parsedDelinquencyAction.getAction())) {
recalculateLoanDelinquencyData(loan);
+ // if pause end date is after current business date, loan delinquency pause flag is changed, emit event
+ if (DateUtils.isAfter(parsedDelinquencyAction.getEndDate(), businessDate)) {
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanDelinquencyRangeChangeBusinessEvent(loan));
+ }
}
businessEventNotifierService.notifyPostBusinessEvent(new LoanAccountDelinquencyPauseChangedBusinessEvent(loan));
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
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 63663d7..67ad3e7 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
@@ -22,6 +22,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyIterable;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -30,6 +31,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -38,23 +40,31 @@
import java.util.Map;
import java.util.Optional;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.ActionContext;
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.LoanAccountDelinquencyPauseChangedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanDelinquencyRangeChangeBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository;
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.LoanDelinquencyAction;
+import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository;
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.helper.DelinquencyEffectivePauseHelper;
+import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatformServiceImpl;
import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService;
+import org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
@@ -100,6 +110,14 @@
private LoanDelinquencyDomainService loanDelinquencyDomainService;
@Mock
private LoanInstallmentDelinquencyTagRepository loanInstallmentDelinquencyTagRepository;
+ @Mock
+ private DelinquencyReadPlatformService delinquencyReadPlatformService;
+ @Mock
+ private DelinquencyActionParseAndValidator delinquencyActionParseAndValidator;
+ @Mock
+ private LoanDelinquencyActionRepository loanDelinquencyActionRepository;
+ @Mock
+ private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@InjectMocks
private DelinquencyWritePlatformServiceImpl underTest;
@@ -498,4 +516,103 @@
assertEquals(loanForProcessing, loanPayloadForEvent);
}
+
+ @Test
+ public void givenLoanAccountWhenBackdatedPauseActionThenLoanDelinquencyPauseChangeBusinessEventIsRaisedTest() {
+ ArgumentCaptor<LoanAccountDelinquencyPauseChangedBusinessEvent> loanDelinquencyPauseChangeEvent = ArgumentCaptor
+ .forClass(LoanAccountDelinquencyPauseChangedBusinessEvent.class);
+ // given
+ Loan loanForProcessing = Mockito.mock(Loan.class);
+ loanForProcessing.setId(1L);
+
+ JsonCommand command = Mockito.mock(JsonCommand.class);
+
+ // Pause period
+ LocalDate startDate = DateUtils.getBusinessLocalDate().minusDays(8);
+ LocalDate endDate = DateUtils.getBusinessLocalDate().minusDays(1);
+
+ List<LoanDelinquencyAction> delinquencyActions = new ArrayList<>();
+ List<LoanDelinquencyActionData> effectiveDelinquency = new ArrayList<>();
+ CollectionData loanCollectionData = CollectionData.template();
+
+ when(loanRepository.findOneWithNotFoundDetection(anyLong())).thenReturn(loanForProcessing);
+
+ when(delinquencyReadPlatformService.retrieveLoanDelinquencyActions(anyLong())).thenReturn(delinquencyActions);
+ LoanDelinquencyAction backdatedPauseAction = Mockito.mock(LoanDelinquencyAction.class);
+ backdatedPauseAction.setId(1L);
+
+ when(delinquencyActionParseAndValidator.validateAndParseUpdate(command, loanForProcessing, delinquencyActions,
+ DateUtils.getBusinessLocalDate())).thenReturn(backdatedPauseAction);
+ when(backdatedPauseAction.getStartDate()).thenReturn(startDate);
+ when(backdatedPauseAction.getEndDate()).thenReturn(endDate);
+ when(backdatedPauseAction.getAction()).thenReturn(DelinquencyAction.PAUSE);
+
+ when(loanDelinquencyActionRepository.saveAndFlush(backdatedPauseAction)).thenReturn(backdatedPauseAction);
+
+ when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(delinquencyActions)).thenReturn(effectiveDelinquency);
+ when(loanDelinquencyDomainService.getOverdueCollectionData(any(), anyList())).thenReturn(loanCollectionData);
+ when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
+
+ // when
+ underTest.createDelinquencyAction(loanForProcessing.getId(), command);
+
+ // then
+ // verify event is raised
+ verify(businessEventNotifierService, times(1)).notifyPostBusinessEvent(loanDelinquencyPauseChangeEvent.capture());
+
+ Loan loanPayloadForEvent = loanDelinquencyPauseChangeEvent.getValue().get();
+ assertEquals(loanForProcessing, loanPayloadForEvent);
+
+ // verify no range change event for pause flag change as both start and end date are backdated
+ verify(businessEventNotifierService, times(0)).notifyPostBusinessEvent(any(LoanDelinquencyRangeChangeBusinessEvent.class));
+
+ }
+
+ @Test
+ public void givenLoanAccountWhenBackdatedPauseActionThenLoanDelinquencyRangeChangeBusinessEventIsRaisedIfPauseFlagChangeTest() {
+ ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDelinquencyRangeChangeEvent = ArgumentCaptor
+ .forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
+ // given
+ Loan loanForProcessing = Mockito.mock(Loan.class);
+ loanForProcessing.setId(1L);
+
+ JsonCommand command = Mockito.mock(JsonCommand.class);
+
+ // Pause period
+ LocalDate startDate = DateUtils.getBusinessLocalDate().minusDays(2);
+ LocalDate endDate = DateUtils.getBusinessLocalDate().plusDays(10);
+
+ List<LoanDelinquencyAction> delinquencyActions = new ArrayList<>();
+ List<LoanDelinquencyActionData> effectiveDelinquency = new ArrayList<>();
+ CollectionData loanCollectionData = CollectionData.template();
+
+ when(loanRepository.findOneWithNotFoundDetection(anyLong())).thenReturn(loanForProcessing);
+
+ when(delinquencyReadPlatformService.retrieveLoanDelinquencyActions(anyLong())).thenReturn(delinquencyActions);
+ LoanDelinquencyAction backdatedPauseAction = Mockito.mock(LoanDelinquencyAction.class);
+ backdatedPauseAction.setId(1L);
+
+ when(delinquencyActionParseAndValidator.validateAndParseUpdate(command, loanForProcessing, delinquencyActions,
+ DateUtils.getBusinessLocalDate())).thenReturn(backdatedPauseAction);
+ when(backdatedPauseAction.getStartDate()).thenReturn(startDate);
+ when(backdatedPauseAction.getEndDate()).thenReturn(endDate);
+ when(backdatedPauseAction.getAction()).thenReturn(DelinquencyAction.PAUSE);
+
+ when(loanDelinquencyActionRepository.saveAndFlush(backdatedPauseAction)).thenReturn(backdatedPauseAction);
+
+ when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(delinquencyActions)).thenReturn(effectiveDelinquency);
+ when(loanDelinquencyDomainService.getOverdueCollectionData(any(), anyList())).thenReturn(loanCollectionData);
+ when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
+
+ // when
+ underTest.createDelinquencyAction(loanForProcessing.getId(), command);
+
+ // then
+ // verify event is raised
+ verify(businessEventNotifierService, times(1)).notifyPostBusinessEvent(loanDelinquencyRangeChangeEvent.capture());
+
+ Loan loanPayloadForEvent = loanDelinquencyRangeChangeEvent.getValue().get();
+ assertEquals(loanForProcessing, loanPayloadForEvent);
+ }
+
}