FINERACT-1971: Delinquency pause validation bug fixes
This commit is contained in:
+14
-1
@@ -61,6 +61,7 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
|
|||||||
} else if (DelinquencyAction.RESUME.equals(parsedDelinquencyAction.getAction())) {
|
} else if (DelinquencyAction.RESUME.equals(parsedDelinquencyAction.getAction())) {
|
||||||
validateResumeStartDate(parsedDelinquencyAction, businessDate);
|
validateResumeStartDate(parsedDelinquencyAction, businessDate);
|
||||||
validateResumeNoEndDate(parsedDelinquencyAction);
|
validateResumeNoEndDate(parsedDelinquencyAction);
|
||||||
|
validateResumeDoesNotExist(parsedDelinquencyAction, savedDelinquencyActions);
|
||||||
validateResumeShouldBeOnActivePause(parsedDelinquencyAction, effectiveDelinquencyList);
|
validateResumeShouldBeOnActivePause(parsedDelinquencyAction, effectiveDelinquencyList);
|
||||||
}
|
}
|
||||||
return parsedDelinquencyAction;
|
return parsedDelinquencyAction;
|
||||||
@@ -87,6 +88,17 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateResumeDoesNotExist(LoanDelinquencyAction parsedDelinquencyAction,
|
||||||
|
List<LoanDelinquencyAction> savedDelinquencyActions) {
|
||||||
|
boolean match = savedDelinquencyActions.stream() //
|
||||||
|
.filter(action -> DelinquencyAction.RESUME.equals(action.getAction())) //
|
||||||
|
.anyMatch(action -> parsedDelinquencyAction.getStartDate().isEqual(action.getStartDate()));
|
||||||
|
if (match) {
|
||||||
|
raiseValidationError("loan-delinquency-action-resume-should-be-unique",
|
||||||
|
"There is an existing Resume Delinquency Action on this date");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void validateResumeNoEndDate(LoanDelinquencyAction parsedDelinquencyAction) {
|
private void validateResumeNoEndDate(LoanDelinquencyAction parsedDelinquencyAction) {
|
||||||
if (parsedDelinquencyAction.getEndDate() != null) {
|
if (parsedDelinquencyAction.getEndDate() != null) {
|
||||||
raiseValidationError("loan-delinquency-action-resume-should-have-no-end-date",
|
raiseValidationError("loan-delinquency-action-resume-should-have-no-end-date",
|
||||||
@@ -163,7 +175,8 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
|
|||||||
*/
|
*/
|
||||||
private boolean isOverlapping(LoanDelinquencyAction parsed, LoanDelinquencyActionData existing) {
|
private boolean isOverlapping(LoanDelinquencyAction parsed, LoanDelinquencyActionData existing) {
|
||||||
return (parsed.getEndDate().isAfter(existing.getStartDate()) && parsed.getEndDate().isBefore(existing.getEndDate()))
|
return (parsed.getEndDate().isAfter(existing.getStartDate()) && parsed.getEndDate().isBefore(existing.getEndDate()))
|
||||||
|| (parsed.getStartDate().isAfter(existing.getStartDate()) && parsed.getStartDate().isBefore(existing.getEndDate()));
|
|| (parsed.getStartDate().isAfter(existing.getStartDate()) && parsed.getStartDate().isBefore(existing.getEndDate()))
|
||||||
|
|| (parsed.getStartDate().isEqual(existing.getStartDate()) && parsed.getEndDate().isEqual(existing.getEndDate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.jetbrains.annotations.NotNull
|
@org.jetbrains.annotations.NotNull
|
||||||
|
|||||||
+43
-1
@@ -36,6 +36,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
@@ -136,6 +137,21 @@ class DelinquencyActionParseAndValidatorTest {
|
|||||||
() -> underTest.validateAndParseUpdate(command, loan, existing, localDate("09 September 2022")));
|
() -> underTest.validateAndParseUpdate(command, loan, existing, localDate("09 September 2022")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNewPauseIsOverlappingWithExistingPauseBecauseSameDates() throws JsonProcessingException {
|
||||||
|
Loan loan = Mockito.mock(Loan.class);
|
||||||
|
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
||||||
|
|
||||||
|
List<LoanDelinquencyAction> existing = List.of(loanDelinquencyAction(PAUSE, "15 September 2022", "22 September 2022"));
|
||||||
|
JsonCommand command = delinquencyAction("pause", "15 September 2022", "22 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",
|
||||||
|
() -> underTest.validateAndParseUpdate(command, loan, existing, localDate("09 September 2022")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNewPauseIsNotOverlappingBecauseThereWasAResume() throws JsonProcessingException {
|
public void testNewPauseIsNotOverlappingBecauseThereWasAResume() throws JsonProcessingException {
|
||||||
Loan loan = Mockito.mock(Loan.class);
|
Loan loan = Mockito.mock(Loan.class);
|
||||||
@@ -214,6 +230,32 @@ class DelinquencyActionParseAndValidatorTest {
|
|||||||
() -> underTest.validateAndParseUpdate(command, loan, List.of(), localDate("10 September 2022")));
|
() -> underTest.validateAndParseUpdate(command, loan, List.of(), localDate("10 September 2022")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidationErrorResumeOnExistingResumeDate() throws JsonProcessingException {
|
||||||
|
Loan loan = Mockito.mock(Loan.class);
|
||||||
|
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, existing,
|
||||||
|
localDate("09 September 2022"));
|
||||||
|
Assertions.assertEquals(RESUME, parsedDelinquencyAction.getAction());
|
||||||
|
Assertions.assertEquals(localDate("09 September 2022"), parsedDelinquencyAction.getStartDate());
|
||||||
|
Assertions.assertNull(parsedDelinquencyAction.getEndDate());
|
||||||
|
|
||||||
|
List<LoanDelinquencyAction> existing2 = List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 September 2022"),
|
||||||
|
loanDelinquencyAction(RESUME, "09 September 2022", null));
|
||||||
|
|
||||||
|
JsonCommand command2 = delinquencyAction("resume", "09 September 2022", null);
|
||||||
|
|
||||||
|
assertPlatformValidationException("There is an existing Resume Delinquency Action on this date",
|
||||||
|
"loan-delinquency-action-resume-should-be-unique",
|
||||||
|
() -> underTest.validateAndParseUpdate(command2, loan, existing2, localDate("09 September 2022")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidationErrorPausePeriodShouldBeAtLeastOneDay() throws JsonProcessingException {
|
public void testValidationErrorPausePeriodShouldBeAtLeastOneDay() throws JsonProcessingException {
|
||||||
Loan loan = Mockito.mock(Loan.class);
|
Loan loan = Mockito.mock(Loan.class);
|
||||||
@@ -348,7 +390,7 @@ class DelinquencyActionParseAndValidatorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private LoanDelinquencyAction loanDelinquencyAction(DelinquencyAction action, String startTime, String endTime) {
|
private LoanDelinquencyAction loanDelinquencyAction(DelinquencyAction action, String startTime, String endTime) {
|
||||||
return new LoanDelinquencyAction(null, action, localDate(startTime), localDate(endTime));
|
return new LoanDelinquencyAction(null, action, localDate(startTime), Objects.isNull(endTime) ? null : localDate(endTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanDelinquencyActionData loanDelinquencyActionData(LoanDelinquencyAction loanDelinquencyAction) {
|
private LoanDelinquencyActionData loanDelinquencyActionData(LoanDelinquencyAction loanDelinquencyAction) {
|
||||||
|
|||||||
+25
@@ -222,6 +222,31 @@ public class DelinquencyActionIntegrationTests extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidationErrorIsThrownWhenCreatingActionThatOverlaps() {
|
||||||
|
runAt("01 January 2023", () -> {
|
||||||
|
// Create Client
|
||||||
|
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
|
|
||||||
|
// Create Loan Product
|
||||||
|
Long loanProductId = createLoanProductWith25PctDownPayment(true, true);
|
||||||
|
|
||||||
|
// Apply and Approve Loan
|
||||||
|
Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 January 2023", 1500.0, 2);
|
||||||
|
|
||||||
|
// Disburse Loan
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 2023");
|
||||||
|
|
||||||
|
// Create Delinquency Pause for the Loan
|
||||||
|
loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, "01 January 2023", "15 January 2023");
|
||||||
|
|
||||||
|
// Create overlapping Delinquency Pause for the Loan
|
||||||
|
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
|
||||||
|
() -> loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, "01 January 2023", "15 January 2023"));
|
||||||
|
assertTrue(exception.getMessage().contains("Delinquency pause period cannot overlap with another pause period"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void validateLoanDelinquencyPausePeriods(Long loanId, GetLoansLoanIdDelinquencyPausePeriod... pausePeriods) {
|
private void validateLoanDelinquencyPausePeriods(Long loanId, GetLoansLoanIdDelinquencyPausePeriod... pausePeriods) {
|
||||||
GetLoansLoanIdResponse loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
GetLoansLoanIdResponse loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
||||||
Assertions.assertNotNull(loan.getDelinquent());
|
Assertions.assertNotNull(loan.getDelinquent());
|
||||||
|
|||||||
Reference in New Issue
Block a user