[FINERACT-1992] Delinquency calculation with pause

This commit is contained in:
taskain7
2023-12-05 16:21:33 +01:00
parent 977fb20171
commit cb40f230b1
22 changed files with 1005 additions and 196 deletions
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
@@ -41,6 +42,9 @@ import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.junit.jupiter.api.BeforeEach;
@@ -61,6 +65,12 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
*/
@Mock
private LoanAccountDomainService loanAccountDomainService;
@Mock
private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@Mock
private DelinquencyReadPlatformService delinquencyReadPlatformService;
@Mock
private BusinessEventNotifierService businessEventNotifierService;
/**
* The class under test.
@@ -84,9 +94,10 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
public void setUp() {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
ThreadLocalContextUtil
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
underTest = new SetLoanDelinquencyTagsBusinessStep(loanAccountDomainService);
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()),
BusinessDateType.COB_DATE, LocalDate.now(ZoneId.systemDefault()))));
underTest = new SetLoanDelinquencyTagsBusinessStep(loanAccountDomainService, delinquencyEffectivePauseHelper,
delinquencyReadPlatformService, businessEventNotifierService);
}
/**
@@ -98,14 +109,14 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
@Test
public void testExecuteSuccessScenario() throws Exception {
// given
doNothing().when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
doNothing().when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class), anyList());
Loan loanForProcessing = createLoan();
// when
Loan processedLoan = underTest.execute(loanForProcessing);
// then
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class), anyList());
assertEquals(processedLoan, loanForProcessing);
}
@@ -137,14 +148,15 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
@Test
public void testExecuteWhenSetLoanDelinquencyTagFails() throws Exception {
// given
doThrow(new RuntimeException()).when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
doThrow(new RuntimeException()).when(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class),
anyList());
Loan loanForProcessing = createLoan();
// when
final Throwable thrownException = assertThrows(RuntimeException.class, () -> underTest.execute(loanForProcessing));
// then
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class));
verify(loanAccountDomainService).setLoanDelinquencyTag(any(Loan.class), any(LocalDate.class), anyList());
assertTrue(thrownException.getClass().isAssignableFrom(RuntimeException.class));
}
@@ -0,0 +1,82 @@
/**
* 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.helper;
import static java.time.Month.JANUARY;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class DelinquencyEffectivePauseHelperTest {
@InjectMocks
private DelinquencyEffectivePauseHelperImpl underTest;
@Test
public void testMultiplePausesWithoutResumeActionCurrentlyInPauseFirstDay() {
// given
List<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
List<LoanDelinquencyActionData> effectivePeriods = underTest.calculateEffectiveDelinquencyList(delinquencyActions);
// then
assertEquals(effectivePeriods.get(0).getStartDate(), LocalDate.of(2023, 1, 10));
assertEquals(effectivePeriods.get(0).getEndDate(), LocalDate.of(2023, 1, 11));
assertEquals(effectivePeriods.get(1).getStartDate(), LocalDate.of(2023, 1, 12));
assertEquals(effectivePeriods.get(1).getEndDate(), LocalDate.of(2023, 1, 13));
assertEquals(effectivePeriods.get(2).getStartDate(), LocalDate.of(2023, 1, 15));
assertEquals(effectivePeriods.get(2).getEndDate(), LocalDate.of(2023, 1, 20));
}
@Test
public void testResumeIsAppliedToOneOfThePauseNotActive() {
// given
List<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, JANUARY, 11), null),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
List<LoanDelinquencyActionData> effectivePeriods = underTest.calculateEffectiveDelinquencyList(delinquencyActions);
// then
assertEquals(effectivePeriods.get(0).getStartDate(), LocalDate.of(2023, 1, 10));
assertEquals(effectivePeriods.get(0).getEndDate(), LocalDate.of(2023, 1, 11));
assertEquals(effectivePeriods.get(1).getStartDate(), LocalDate.of(2023, 1, 13));
assertEquals(effectivePeriods.get(1).getEndDate(), LocalDate.of(2023, 1, 14));
assertEquals(effectivePeriods.get(2).getStartDate(), LocalDate.of(2023, 1, 15));
assertEquals(effectivePeriods.get(2).getEndDate(), LocalDate.of(2023, 1, 20));
}
}
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.delinquency.service;
import static java.time.Month.JANUARY;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
import java.time.LocalDate;
import java.util.Arrays;
@@ -35,6 +34,7 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquen
import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper;
import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper;
import org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.DelinquencyPausePeriod;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
@@ -82,22 +82,25 @@ class DelinquencyReadPlatformServiceImplTest {
public void testNoEnrichmentWhenThereIsNoDelinquencyAction() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of();
Collection<LoanDelinquencyActionData> delinquencyActions = List.of();
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
Assertions.assertNull(collectionData.getDelinquencyPausePeriods());
Assertions.assertTrue(collectionData.getDelinquencyPausePeriods().isEmpty());
}
@Test
public void testMultiplePausesWithoutResumeActionCurrentlyInPauseFirstDay() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
@@ -114,10 +117,13 @@ class DelinquencyReadPlatformServiceImplTest {
public void testMultiplePausesWithoutResumeActionCurrentlyInPauseLastDay() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 13))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 13));
@@ -133,10 +139,13 @@ class DelinquencyReadPlatformServiceImplTest {
public void testMultiplePausesWithoutResumeActionCurrentBusinessDateBetweenStartAndEndDate() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 12), LocalDate.of(2023, JANUARY, 14))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 13));
@@ -152,10 +161,13 @@ class DelinquencyReadPlatformServiceImplTest {
public void testMultiplePausesWithoutResumeCurrentBusinessDateIsNotOverlappingWithAnyOfThePauses() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
Collection<LoanDelinquencyActionData> delinquencyActions = List.of(
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 11))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14))),
new LoanDelinquencyActionData(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20))));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
@@ -168,48 +180,6 @@ class DelinquencyReadPlatformServiceImplTest {
);
}
@Test
public void testResumeIsAppliedToOneOfThePauseNotActive() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, JANUARY, 11), null),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12));
// then
verifyPausePeriods(collectionData, //
pausePeriod(false, "2023-01-10", "2023-01-11"), //
pausePeriod(false, "2023-01-13", "2023-01-14"), //
pausePeriod(false, "2023-01-15", "2023-01-20") //
);
}
@Test
public void testResumeIsAppliedToOneOfThePauseActive() {
// given
CollectionData collectionData = CollectionData.template();
Collection<LoanDelinquencyAction> delinquencyActions = List.of(
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 10), LocalDate.of(2023, JANUARY, 20)),
new LoanDelinquencyAction(null, RESUME, LocalDate.of(2023, JANUARY, 11), null),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 13), LocalDate.of(2023, JANUARY, 14)),
new LoanDelinquencyAction(null, PAUSE, LocalDate.of(2023, JANUARY, 15), LocalDate.of(2023, JANUARY, 20)));
// when
underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 11));
// then
verifyPausePeriods(collectionData, //
pausePeriod(true, "2023-01-10", "2023-01-11"), //
pausePeriod(false, "2023-01-13", "2023-01-14"), //
pausePeriod(false, "2023-01-15", "2023-01-20") //
);
}
private void verifyPausePeriods(CollectionData collectionData, DelinquencyPausePeriod... pausePeriods) {
if (pausePeriods.length > 0) {
Assertions.assertEquals(Arrays.asList(pausePeriods), collectionData.getDelinquencyPausePeriods());
@@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction;
import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.jetbrains.annotations.NotNull;
@@ -54,7 +55,9 @@ import org.mockito.Mockito;
class DelinquencyActionParseAndValidatorTest {
private final FromJsonHelper fromJsonHelper = new FromJsonHelper();
private final DelinquencyActionParseAndValidator underTest = new DelinquencyActionParseAndValidator(fromJsonHelper);
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper = Mockito.mock(DelinquencyEffectivePauseHelper.class);
private final DelinquencyActionParseAndValidator underTest = new DelinquencyActionParseAndValidator(fromJsonHelper,
delinquencyEffectivePauseHelper);
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.US);
@Test
@@ -77,9 +80,12 @@ class DelinquencyActionParseAndValidatorTest {
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
JsonCommand command = delinquencyAction("resume", "09 September 2022", null);
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 September 2022"));
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
LoanDelinquencyAction parsedDelinquencyAction = underTest.validateAndParseUpdate(command, loan,
List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 September 2022")), localDate("09 September 2022"));
LoanDelinquencyAction parsedDelinquencyAction = underTest.validateAndParseUpdate(command, loan, existing,
localDate("09 September 2022"));
Assertions.assertEquals(RESUME, parsedDelinquencyAction.getAction());
Assertions.assertEquals(localDate("09 September 2022"), parsedDelinquencyAction.getStartDate());
Assertions.assertNull(parsedDelinquencyAction.getEndDate());
@@ -92,6 +98,8 @@ class DelinquencyActionParseAndValidatorTest {
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "14 September 2022", "22 September 2022"));
JsonCommand command = delinquencyAction("pause", "09 September 2022", "15 September 2022");
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
assertPlatformValidationException("Delinquency pause period cannot overlap with another pause period",
"loan-delinquency-action-overlapping",
@@ -105,6 +113,8 @@ class DelinquencyActionParseAndValidatorTest {
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "14 September 2022", "22 September 2022"));
JsonCommand command = delinquencyAction("pause", "15 September 2022", "23 September 2022");
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
assertPlatformValidationException("Delinquency pause period cannot overlap with another pause period",
"loan-delinquency-action-overlapping",
@@ -118,6 +128,8 @@ class DelinquencyActionParseAndValidatorTest {
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "15 September 2022", "22 September 2022"));
JsonCommand command = delinquencyAction("pause", "13 September 2022", "20 September 2022");
List<LoanDelinquencyActionData> effectiveList = List.of(loanDelinquencyActionData(existing.get(0)));
Mockito.when(delinquencyEffectivePauseHelper.calculateEffectiveDelinquencyList(existing)).thenReturn(effectiveList);
assertPlatformValidationException("Delinquency pause period cannot overlap with another pause period",
"loan-delinquency-action-overlapping",
@@ -339,6 +351,10 @@ class DelinquencyActionParseAndValidatorTest {
return new LoanDelinquencyAction(null, action, localDate(startTime), localDate(endTime));
}
private LoanDelinquencyActionData loanDelinquencyActionData(LoanDelinquencyAction loanDelinquencyAction) {
return new LoanDelinquencyActionData(loanDelinquencyAction);
}
private LoanDelinquencyAction loanDelinquencyAction(DelinquencyAction action, String startTime) {
return new LoanDelinquencyAction(null, action, localDate(startTime), null);
}
@@ -31,6 +31,7 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -56,6 +57,7 @@ import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatfor
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.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
@@ -115,6 +117,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDeliquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
DelinquencyRange range1 = DelinquencyRange.instance("Range1", 1, 2);
@@ -140,10 +143,11 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(false);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -155,6 +159,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
@Test
public void givenLoanAccountWithDelinquencyBucketWhenNoRangeChangeThenNoEventIsRaised() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
@@ -181,10 +186,11 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanProduct.getDelinquencyBucket()).thenReturn(delinquencyBucket);
when(loanForProcessing.hasDelinquencyBucket()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(0)).saveAllAndFlush(anyIterable());
@@ -194,6 +200,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
@Test
public void givenLoanAccountWithNoDelinquencyBucketThenNoEventIsRaised() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
Loan loanForProcessing = Mockito.mock(Loan.class);
LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate();
@@ -203,7 +210,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.hasDelinquencyBucket()).thenReturn(false);
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(0)).saveAllAndFlush(anyIterable());
@@ -218,6 +225,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDelinquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
DelinquencyRange range1 = DelinquencyRange.instance("Range1", 1, 2);
@@ -260,12 +268,13 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(0)))
.thenReturn(Optional.empty());
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -293,6 +302,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDelinquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
DelinquencyRange range1 = DelinquencyRange.instance("Range1", 1, 2);
@@ -338,12 +348,13 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(0)))
.thenReturn(Optional.of(previousInstallmentTag));
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -374,6 +385,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDelinquencyRangeChangeEvent = ArgumentCaptor
.forClass(LoanDelinquencyRangeChangeBusinessEvent.class);
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
// given
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
@@ -431,14 +443,15 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanForProcessing.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loanDelinquencyTagRepository.findByLoanAndLiftedOnDate(any(), any())).thenReturn(Optional.empty());
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing)).thenReturn(loanDelinquencyData);
when(loanDelinquencyDomainService.getLoanDelinquencyData(loanForProcessing, effectiveDelinquencyList))
.thenReturn(loanDelinquencyData);
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(0)))
.thenReturn(Optional.of(previousInstallmentTag_1));
when(loanInstallmentDelinquencyTagRepository.findByLoanAndInstallment(loanForProcessing, repaymentScheduleInstallments.get(1)))
.thenReturn(Optional.of(previousInstallmentTag));
// when
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData);
underTest.applyDelinquencyTagToLoan(loanScheduleDelinquencyData, effectiveDelinquencyList);
// then
verify(loanDelinquencyTagRepository, times(1)).saveAllAndFlush(anyIterable());
@@ -42,7 +42,9 @@ import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper;
import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainServiceImpl;
import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -75,6 +77,8 @@ public class LoanDelinquencyDomainServiceTest {
private Loan loan;
@Mock
private LoanProduct loanProduct;
@Mock
private DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
@InjectMocks
private LoanDelinquencyDomainServiceImpl underTest;
@@ -107,6 +111,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanAccountWithoutOverdueThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
final LocalDate fromDate = businessDate.minusMonths(1);
final LocalDate dueDate = businessDate;
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = Arrays.asList(new LoanRepaymentScheduleInstallment(loan, 1,
@@ -118,7 +123,7 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loan.getCurrency()).thenReturn(currency);
CollectionData collectionData = underTest.getOverdueCollectionData(loan);
CollectionData collectionData = underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
// then
assertEquals(0L, collectionData.getDelinquentDays());
@@ -130,6 +135,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanAccountWithOverdueThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
final Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).minusDays(daysDiff);
final LocalDate dueDate = businessDate.minusDays(daysDiff);
@@ -143,8 +149,9 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Collections.emptyList());
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency);
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
CollectionData collectionData = underTest.getOverdueCollectionData(loan);
CollectionData collectionData = underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
// then
assertEquals(daysDiff, collectionData.getDelinquentDays());
@@ -156,6 +163,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanAccountWithoutOverdueWithChargebackThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
PaymentDetail paymentDetail = Mockito.mock(PaymentDetail.class);
Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).plusDays(daysDiff);
@@ -178,7 +186,7 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loan.getCurrency()).thenReturn(currency);
CollectionData collectionData = underTest.getOverdueCollectionData(loan);
CollectionData collectionData = underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
// then
assertEquals(0L, collectionData.getDelinquentDays());
@@ -190,6 +198,7 @@ public class LoanDelinquencyDomainServiceTest {
@Test
public void givenLoanInstallmentWithOverdueEnableInstallmentDelinquencyThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
final Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).minusDays(daysDiff);
final LocalDate dueDate = businessDate.minusDays(daysDiff);
@@ -207,8 +216,9 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency);
when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList);
// then
assertNotNull(collectionData);
@@ -232,6 +242,7 @@ public class LoanDelinquencyDomainServiceTest {
public void givenLoanInstallmentWithoutOverdueWithChargebackAndEnableInstallmentDelinquencyThenCalculateDelinquentData() {
// given
final List<LoanDelinquencyActionData> effectiveDelinquencyList = Collections.emptyList();
PaymentDetail paymentDetail = Mockito.mock(PaymentDetail.class);
Long daysDiff = 2L;
final LocalDate fromDate = businessDate.minusMonths(1).plusDays(daysDiff);
@@ -255,8 +266,9 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loan.getCurrency()).thenReturn(currency);
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Arrays.asList(loanTransaction));
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan);
LoanDelinquencyData collectionData = underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList);
// then
assertNotNull(collectionData);