From 06a56d106d8f2590c4232f838ab85e3794108f25 Mon Sep 17 00:00:00 2001 From: Peter Bagrij Date: Mon, 20 Nov 2023 18:59:04 +0100 Subject: [PATCH] FINERACT-1992 Improve validation errors. --- .../DelinquencyActionParseAndValidator.java | 39 +++++++++++++++---- ...elinquencyActionParseAndValidatorTest.java | 38 +++++++++++++++--- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidator.java index eac6670e5..163ee2730 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidator.java @@ -19,6 +19,9 @@ package org.apache.fineract.portfolio.delinquency.validator; import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME; +import static org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.ACTION; +import static org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.END_DATE; +import static org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParameters.START_DATE; import com.google.gson.JsonElement; import jakarta.validation.constraints.NotNull; @@ -54,6 +57,7 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator { LoanDelinquencyAction parsedDelinquencyAction = parseCommand(command); validateLoanIsActive(loan); if (DelinquencyAction.PAUSE.equals(parsedDelinquencyAction.getAction())) { + validateBothStartAndEndDatesAreProvided(parsedDelinquencyAction); validatePauseStartAndEndDate(parsedDelinquencyAction, businessDate); validatePauseShallNotOverlap(parsedDelinquencyAction, effectiveDelinquencyList); } else if (DelinquencyAction.RESUME.equals(parsedDelinquencyAction.getAction())) { @@ -64,6 +68,16 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator { return parsedDelinquencyAction; } + private void validateBothStartAndEndDatesAreProvided(LoanDelinquencyAction parsedDelinquencyAction) { + if (parsedDelinquencyAction.getStartDate() == null) { + raiseValidationError("loan-delinquency-action-pause-startDate-cannot-be-blank", "The parameter `startDate` is mandatory", + START_DATE); + } + if (parsedDelinquencyAction.getEndDate() == null) { + raiseValidationError("loan-delinquency-action-pause-endDate-cannot-be-blank", "The parameter `endDate` is mandatory", END_DATE); + } + } + private List calculateEffectiveDelinquencyList(List savedDelinquencyActions) { // partition them based on type Map> partitioned = savedDelinquencyActions.stream() @@ -106,14 +120,18 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator { private void validateResumeNoEndDate(LoanDelinquencyAction parsedDelinquencyAction) { if (parsedDelinquencyAction.getEndDate() != null) { raiseValidationError("loan-delinquency-action-resume-should-have-no-end-date", - "Resume Delinquency action can not have end date"); + "Resume Delinquency action can not have end date", END_DATE); } } private void validateResumeStartDate(LoanDelinquencyAction parsedDelinquencyAction, LocalDate businessDate) { + if (parsedDelinquencyAction.getStartDate() == null) { + raiseValidationError("loan-delinquency-action-resume-startDate-cannot-be-blank", "The parameter `startDate` is mandatory", + START_DATE); + } if (!parsedDelinquencyAction.getStartDate().equals(businessDate)) { raiseValidationError("loan-delinquency-action-invalid-start-date", - "Start date of the Resume Delinquency action must be the current business date"); + "Start date of the Resume Delinquency action must be the current business date", START_DATE); } } @@ -124,7 +142,8 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator { } if (businessDate.isAfter(parsedDelinquencyAction.getStartDate())) { - raiseValidationError("loan-delinquency-action-invalid-start-date", "Start date of pause period must be in the future"); + raiseValidationError("loan-delinquency-action-invalid-start-date", "Start date of pause period must be in the future", + START_DATE); } } @@ -167,22 +186,21 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator { } else if ("resume".equalsIgnoreCase(actionString)) { return DelinquencyAction.RESUME; } else { - throw new PlatformApiDataValidationException(List.of(ApiParameterError.generalError("loan-delinquency-action-invalid-action", - "Invalid Delinquency Action: " + actionString))); + throw new PlatformApiDataValidationException(List.of(ApiParameterError.parameterError("loan-delinquency-action-invalid-action", + "Invalid Delinquency Action: " + actionString, ACTION))); } } private void validateActionString(String actionString) { if (StringUtils.isEmpty(actionString)) { - raiseValidationError("loan-delinquency-action-missing-action", "Delinquency Action must not be null or empty"); + raiseValidationError("loan-delinquency-action-missing-action", "Delinquency Action must not be null or empty", ACTION); } } private LocalDate extractStartDate(JsonElement json) { String dateFormat = jsonHelper.extractStringNamed(DelinquencyActionParameters.DATE_FORMAT, json); String locale = jsonHelper.extractStringNamed(DelinquencyActionParameters.LOCALE, json); - return jsonHelper.extractLocalDateNamed(DelinquencyActionParameters.START_DATE, json, dateFormat, - JsonParserHelper.localeFromString(locale)); + return jsonHelper.extractLocalDateNamed(START_DATE, json, dateFormat, JsonParserHelper.localeFromString(locale)); } private LocalDate extractEndDate(JsonElement json) { @@ -196,4 +214,9 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator { throw new PlatformApiDataValidationException(List.of(ApiParameterError.generalError(globalisationMessageCode, msg))); } + private void raiseValidationError(String globalisationMessageCode, String msg, String fieldName) + throws PlatformApiDataValidationException { + throw new PlatformApiDataValidationException(List.of(ApiParameterError.parameterError(globalisationMessageCode, msg, fieldName))); + } + } diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidatorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidatorTest.java index daa161095..cc8ee2117 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidatorTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/validator/DelinquencyActionParseAndValidatorTest.java @@ -36,6 +36,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; @@ -44,6 +45,7 @@ import org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyAction; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; @@ -78,7 +80,7 @@ class DelinquencyActionParseAndValidatorTest { LoanDelinquencyAction parsedDelinquencyAction = underTest.validateAndParseUpdate(command, loan, List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 September 2022")), localDate("09 September 2022")); - Assertions.assertEquals(DelinquencyAction.RESUME, parsedDelinquencyAction.getAction()); + Assertions.assertEquals(RESUME, parsedDelinquencyAction.getAction()); Assertions.assertEquals(localDate("09 September 2022"), parsedDelinquencyAction.getStartDate()); Assertions.assertNull(parsedDelinquencyAction.getEndDate()); } @@ -249,14 +251,40 @@ class DelinquencyActionParseAndValidatorTest { () -> underTest.validateAndParseUpdate(command, loan, List.of(), localDate("09 September 2022"))); } + @Test + public void testStartDateOrEndDateIsMissingForPause() { + Loan loan = Mockito.mock(Loan.class); + Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE); + + assertPlatformValidationException("The parameter `startDate` is mandatory", + "loan-delinquency-action-pause-startDate-cannot-be-blank", + () -> underTest.validateAndParseUpdate(delinquencyAction("pause", null, "09 September 2022"), loan, List.of(), + localDate("09 September 2022"))); + + assertPlatformValidationException("The parameter `endDate` is mandatory", "loan-delinquency-action-pause-endDate-cannot-be-blank", + () -> underTest.validateAndParseUpdate(delinquencyAction("pause", "09 September 2022", null), loan, List.of(), + localDate("09 September 2022"))); + } + + @Test + public void testStartDateIsMissingForResume() { + Loan loan = Mockito.mock(Loan.class); + Mockito.when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE); + + assertPlatformValidationException("The parameter `startDate` is mandatory", + "loan-delinquency-action-resume-startDate-cannot-be-blank", () -> underTest + .validateAndParseUpdate(delinquencyAction("resume", null, null), loan, List.of(), localDate("09 September 2022"))); + } + @NotNull - private JsonCommand delinquencyAction(String action, String startDate, String endDate) throws JsonProcessingException { + private JsonCommand delinquencyAction(@Nullable String action, @Nullable String startDate, @Nullable String endDate) + throws JsonProcessingException { Map map = new HashMap<>(); - map.put(ACTION, action); + Optional.ofNullable(action).ifPresent(a -> map.put(ACTION, a)); map.put(DATE_FORMAT, "dd MMMM yyyy"); map.put(LOCALE, "en"); - map.put(START_DATE, startDate); - map.put(END_DATE, endDate); + Optional.ofNullable(startDate).ifPresent(sd -> map.put(START_DATE, sd)); + Optional.ofNullable(endDate).ifPresent(ed -> map.put(END_DATE, ed)); return createJsonCommand(map); }