FINERACT-1992 Improve validation errors.
This commit is contained in:
+31
-8
@@ -19,6 +19,9 @@
|
|||||||
package org.apache.fineract.portfolio.delinquency.validator;
|
package org.apache.fineract.portfolio.delinquency.validator;
|
||||||
|
|
||||||
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
|
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 com.google.gson.JsonElement;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
@@ -54,6 +57,7 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
|
|||||||
LoanDelinquencyAction parsedDelinquencyAction = parseCommand(command);
|
LoanDelinquencyAction parsedDelinquencyAction = parseCommand(command);
|
||||||
validateLoanIsActive(loan);
|
validateLoanIsActive(loan);
|
||||||
if (DelinquencyAction.PAUSE.equals(parsedDelinquencyAction.getAction())) {
|
if (DelinquencyAction.PAUSE.equals(parsedDelinquencyAction.getAction())) {
|
||||||
|
validateBothStartAndEndDatesAreProvided(parsedDelinquencyAction);
|
||||||
validatePauseStartAndEndDate(parsedDelinquencyAction, businessDate);
|
validatePauseStartAndEndDate(parsedDelinquencyAction, businessDate);
|
||||||
validatePauseShallNotOverlap(parsedDelinquencyAction, effectiveDelinquencyList);
|
validatePauseShallNotOverlap(parsedDelinquencyAction, effectiveDelinquencyList);
|
||||||
} else if (DelinquencyAction.RESUME.equals(parsedDelinquencyAction.getAction())) {
|
} else if (DelinquencyAction.RESUME.equals(parsedDelinquencyAction.getAction())) {
|
||||||
@@ -64,6 +68,16 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
|
|||||||
return parsedDelinquencyAction;
|
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<LoanDelinquencyActionData> calculateEffectiveDelinquencyList(List<LoanDelinquencyAction> savedDelinquencyActions) {
|
private List<LoanDelinquencyActionData> calculateEffectiveDelinquencyList(List<LoanDelinquencyAction> savedDelinquencyActions) {
|
||||||
// partition them based on type
|
// partition them based on type
|
||||||
Map<DelinquencyAction, List<LoanDelinquencyAction>> partitioned = savedDelinquencyActions.stream()
|
Map<DelinquencyAction, List<LoanDelinquencyAction>> partitioned = savedDelinquencyActions.stream()
|
||||||
@@ -106,14 +120,18 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
|
|||||||
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",
|
||||||
"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) {
|
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)) {
|
if (!parsedDelinquencyAction.getStartDate().equals(businessDate)) {
|
||||||
raiseValidationError("loan-delinquency-action-invalid-start-date",
|
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())) {
|
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)) {
|
} else if ("resume".equalsIgnoreCase(actionString)) {
|
||||||
return DelinquencyAction.RESUME;
|
return DelinquencyAction.RESUME;
|
||||||
} else {
|
} else {
|
||||||
throw new PlatformApiDataValidationException(List.of(ApiParameterError.generalError("loan-delinquency-action-invalid-action",
|
throw new PlatformApiDataValidationException(List.of(ApiParameterError.parameterError("loan-delinquency-action-invalid-action",
|
||||||
"Invalid Delinquency Action: " + actionString)));
|
"Invalid Delinquency Action: " + actionString, ACTION)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateActionString(String actionString) {
|
private void validateActionString(String actionString) {
|
||||||
if (StringUtils.isEmpty(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) {
|
private LocalDate extractStartDate(JsonElement json) {
|
||||||
String dateFormat = jsonHelper.extractStringNamed(DelinquencyActionParameters.DATE_FORMAT, json);
|
String dateFormat = jsonHelper.extractStringNamed(DelinquencyActionParameters.DATE_FORMAT, json);
|
||||||
String locale = jsonHelper.extractStringNamed(DelinquencyActionParameters.LOCALE, json);
|
String locale = jsonHelper.extractStringNamed(DelinquencyActionParameters.LOCALE, json);
|
||||||
return jsonHelper.extractLocalDateNamed(DelinquencyActionParameters.START_DATE, json, dateFormat,
|
return jsonHelper.extractLocalDateNamed(START_DATE, json, dateFormat, JsonParserHelper.localeFromString(locale));
|
||||||
JsonParserHelper.localeFromString(locale));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate extractEndDate(JsonElement json) {
|
private LocalDate extractEndDate(JsonElement json) {
|
||||||
@@ -196,4 +214,9 @@ public class DelinquencyActionParseAndValidator extends ParseAndValidator {
|
|||||||
throw new PlatformApiDataValidationException(List.of(ApiParameterError.generalError(globalisationMessageCode, msg)));
|
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)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-5
@@ -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.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;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
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.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
@@ -78,7 +80,7 @@ class DelinquencyActionParseAndValidatorTest {
|
|||||||
|
|
||||||
LoanDelinquencyAction parsedDelinquencyAction = underTest.validateAndParseUpdate(command, loan,
|
LoanDelinquencyAction parsedDelinquencyAction = underTest.validateAndParseUpdate(command, loan,
|
||||||
List.of(loanDelinquencyAction(PAUSE, "05 September 2022", "15 September 2022")), localDate("09 September 2022"));
|
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.assertEquals(localDate("09 September 2022"), parsedDelinquencyAction.getStartDate());
|
||||||
Assertions.assertNull(parsedDelinquencyAction.getEndDate());
|
Assertions.assertNull(parsedDelinquencyAction.getEndDate());
|
||||||
}
|
}
|
||||||
@@ -249,14 +251,40 @@ class DelinquencyActionParseAndValidatorTest {
|
|||||||
() -> underTest.validateAndParseUpdate(command, loan, List.of(), localDate("09 September 2022")));
|
() -> 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
|
@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<String, Object> map = new HashMap<>();
|
Map<String, Object> 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(DATE_FORMAT, "dd MMMM yyyy");
|
||||||
map.put(LOCALE, "en");
|
map.put(LOCALE, "en");
|
||||||
map.put(START_DATE, startDate);
|
Optional.ofNullable(startDate).ifPresent(sd -> map.put(START_DATE, sd));
|
||||||
map.put(END_DATE, endDate);
|
Optional.ofNullable(endDate).ifPresent(ed -> map.put(END_DATE, ed));
|
||||||
return createJsonCommand(map);
|
return createJsonCommand(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user