From 3e923bf483205f8bb1f6415d0247a87ce481baf7 Mon Sep 17 00:00:00 2001 From: Janos Meszaros Date: Sat, 10 Aug 2024 15:15:29 +0200 Subject: [PATCH] FINERACT-2114: Interest rate modification, adjust reschedule validation --- .../infrastructure/core/api/JsonCommand.java | 4 + .../core/serialization/FromJsonHelper.java | 7 ++ .../core/serialization/JsonParserHelper.java | 21 ++++ .../serialization/JsonParserHelperTest.java | 95 +++++++++++++++ .../portfolio/loanaccount/domain/Loan.java | 15 +++ .../RescheduleLoansApiConstants.java | 5 +- .../LoanRescheduleRequestDataValidator.java | 115 +++++++++++++----- ...heduleRequestWritePlatformServiceImpl.java | 21 +--- 8 files changed, 237 insertions(+), 46 deletions(-) create mode 100644 fineract-core/src/test/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelperTest.java diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java index 6cfeb4790..46f9f2ddb 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/api/JsonCommand.java @@ -262,6 +262,10 @@ public final class JsonCommand { return parameterExists(parameterName); } + public boolean hasParameterValue(final String parameterName) { + return this.fromApiJsonHelper.parameterHasValue(parameterName, this.parsedCommand); + } + public String dateFormat() { return stringValueOfParameterNamed("dateFormat"); } diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java index 239276d0b..5bd35d56d 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java @@ -157,6 +157,13 @@ public class FromJsonHelper { return this.helperDelegator.parameterExists(parameterName, element); } + /** + * Check Parameter has a non-blank value + */ + public boolean parameterHasValue(final String parameterName, final JsonElement element) { + return this.helperDelegator.parameterHasValue(parameterName, element); + } + public String extractStringNamed(final String parameterName, final JsonElement element) { return this.helperDelegator.extractStringNamed(parameterName, element, new HashSet()); } diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java index a3cd2bd41..e0573cd7c 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java @@ -61,6 +61,27 @@ public class JsonParserHelper { return element.getAsJsonObject().has(parameterName); } + /** + * Check Parameter has a non-blank value + */ + public boolean parameterHasValue(final String parameterName, final JsonElement element) { + if (element == null || !element.isJsonObject()) { + return false; + } + + var valueObject = element.getAsJsonObject().get(parameterName); + if (valueObject == null || valueObject.isJsonNull()) { + return false; + } + if (valueObject instanceof JsonArray) { + return !valueObject.getAsJsonArray().isEmpty(); + } + if (valueObject instanceof JsonObject) { + return !valueObject.getAsJsonObject().isEmpty(); + } + return valueObject.isJsonPrimitive() && !valueObject.getAsJsonPrimitive().getAsString().isBlank(); + } + public Boolean extractBooleanNamed(final String parameterName, final JsonElement element, final Set requestParamatersDetected) { Boolean value = null; if (element.isJsonObject()) { diff --git a/fineract-core/src/test/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelperTest.java b/fineract-core/src/test/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelperTest.java new file mode 100644 index 000000000..3ed490d7e --- /dev/null +++ b/fineract-core/src/test/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelperTest.java @@ -0,0 +1,95 @@ +/** + * 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.infrastructure.core.serialization; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class JsonParserHelperTest { + + static final JsonParserHelper onTestUnit = new JsonParserHelper(); + + static final String testDataHasValues = "{\n" + " \"integerNumber\": 10,\n" + " \"doubleNumber\": 3.14,\n" + + " \"string\": \"example\",\n" + " \"arrayList\": [1, \"two\", 3.0],\n" + " \"localDate\": \"2024-08-10\",\n" + + " \"keyValue\": {\n" + " \"key\": \"sampleKey\",\n" + " \"value\": \"sampleValue\"\n" + " }\n" + "}"; + + static final String testDataValuesEmpty = "{\n" + " \"integerNumber\": null,\n" + " \"doubleNumber\": 0.0,\n" + + " \"string\": \"\",\n" + " \"arrayList\": [],\n" + " \"localDate\": null,\n" + " \"keyValue\": {\n" + + " \"key\": \"\",\n" + " \"value\": null\n" + " }\n" + "}"; + + static final String testDataValuesMissing = "{}"; + + @Test + void parameterExists() { + JsonElement jsonHasValues = JsonParser.parseString(testDataHasValues); + JsonElement jsonValuesEmpty = JsonParser.parseString(testDataValuesEmpty); + JsonElement jsonValuesMissing = JsonParser.parseString(testDataValuesMissing); + + Assertions.assertTrue(onTestUnit.parameterExists("integerNumber", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterExists("doubleNumber", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterExists("string", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterExists("arrayList", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterExists("localDate", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterExists("keyValue", jsonHasValues)); + + Assertions.assertTrue(onTestUnit.parameterExists("integerNumber", jsonValuesEmpty)); + Assertions.assertTrue(onTestUnit.parameterExists("doubleNumber", jsonValuesEmpty)); + Assertions.assertTrue(onTestUnit.parameterExists("string", jsonValuesEmpty)); + Assertions.assertTrue(onTestUnit.parameterExists("arrayList", jsonValuesEmpty)); + Assertions.assertTrue(onTestUnit.parameterExists("localDate", jsonValuesEmpty)); + Assertions.assertTrue(onTestUnit.parameterExists("keyValue", jsonValuesEmpty)); + + Assertions.assertFalse(onTestUnit.parameterExists("integerNumber", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterExists("doubleNumber", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterExists("string", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterExists("arrayList", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterExists("localDate", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterExists("keyValue", jsonValuesMissing)); + } + + @Test + void parameterHasValue() { + JsonElement jsonHasValues = JsonParser.parseString(testDataHasValues); + JsonElement jsonValuesEmpty = JsonParser.parseString(testDataValuesEmpty); + JsonElement jsonValuesMissing = JsonParser.parseString(testDataValuesMissing); + + Assertions.assertTrue(onTestUnit.parameterHasValue("integerNumber", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterHasValue("doubleNumber", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterHasValue("string", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterHasValue("arrayList", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterHasValue("localDate", jsonHasValues)); + Assertions.assertTrue(onTestUnit.parameterHasValue("keyValue", jsonHasValues)); + + Assertions.assertFalse(onTestUnit.parameterHasValue("integerNumber", jsonValuesEmpty)); + Assertions.assertTrue(onTestUnit.parameterHasValue("doubleNumber", jsonValuesEmpty)); + Assertions.assertFalse(onTestUnit.parameterHasValue("string", jsonValuesEmpty)); + Assertions.assertFalse(onTestUnit.parameterHasValue("arrayList", jsonValuesEmpty)); + Assertions.assertFalse(onTestUnit.parameterHasValue("localDate", jsonValuesEmpty)); + Assertions.assertTrue(onTestUnit.parameterHasValue("keyValue", jsonValuesEmpty)); + + Assertions.assertFalse(onTestUnit.parameterHasValue("integerNumber", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterHasValue("doubleNumber", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterHasValue("string", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterHasValue("arrayList", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterHasValue("localDate", jsonValuesMissing)); + Assertions.assertFalse(onTestUnit.parameterHasValue("keyValue", jsonValuesMissing)); + } +} diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 80fb57c04..008f9537e 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -4566,6 +4566,21 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return installment; } + /** + * @param date + * @return a schedule installment is related to the provided date + **/ + public LoanRepaymentScheduleInstallment getRelatedRepaymentScheduleInstallment(LocalDate date) { + if (date == null) { + return null; + } + return getRepaymentScheduleInstallments()// + .stream()// + .filter(installment -> date.isAfter(installment.getFromDate()) && !date.isAfter(installment.getDueDate()))// + .findAny()// + .orElse(null);// + } + /** * @return loan disbursement data **/ diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/RescheduleLoansApiConstants.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/RescheduleLoansApiConstants.java index 87f4ebbb0..eb0e41045 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/RescheduleLoansApiConstants.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/RescheduleLoansApiConstants.java @@ -50,8 +50,9 @@ public final class RescheduleLoansApiConstants { public static final String rescheduleReasonCommentParamName = "rescheduleReasonComment"; public static final String submittedOnDateParamName = "submittedOnDate"; public static final String adjustedDueDateParamName = "adjustedDueDate"; - public static final String resheduleForMultiDisbursementNotSupportedErrorCode = "loan.reschedule.tranche.multidisbursement.error.code"; - public static final String resheduleWithInterestRecalculationNotSupportedErrorCode = "loan.reschedule.interestrecalculation.error.code"; + public static final String rescheduleForMultiDisbursementNotSupportedErrorCode = "loan.reschedule.tranche.multidisbursement.error.code"; + public static final String rescheduleMultipleOperationsNotSupportedErrorCode = "loan.reschedule.multioperations.error.code"; + public static final String rescheduleSelectedOperationNotSupportedErrorCode = "loan.reschedule.selectedoperationnotsupported.error.code"; public static final String allCommandParamName = "all"; public static final String approveCommandParamName = "approve"; public static final String pendingCommandParamName = "pending"; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java index 7f6f57abf..531225711 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java @@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; import org.springframework.stereotype.Component; @@ -140,11 +141,77 @@ public class LoanRescheduleRequestDataValidator { dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleReasonCommentParamName).value(rescheduleReasonComment) .ignoreIfNull().notExceedingLengthOf(500); - if (this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.emiParamName, jsonElement) - || this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.endDateParamName, jsonElement)) { + final LocalDate adjustedDueDate = this.fromJsonHelper.extractLocalDateNamed(RescheduleLoansApiConstants.adjustedDueDateParamName, + jsonElement); + + if (adjustedDueDate != null && DateUtils.isBefore(adjustedDueDate, rescheduleFromDate)) { + dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName).failWithCode( + "adjustedDueDate.before.rescheduleFromDate", "Adjusted due date cannot be before the reschedule from date"); + } + + if (loan.getLoanProduct().getLoanProductRelatedDetail().getLoanScheduleType() == LoanScheduleType.CUMULATIVE) { final LocalDate endDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.endDateParamName); final BigDecimal emi = jsonCommand.bigDecimalValueOfParameterNamed(RescheduleLoansApiConstants.emiParamName); + validateForCumulativeLoan(dataValidatorBuilder, loan, jsonElement, rescheduleFromDate, endDate, emi); + } else { + validateForProgressiveLoan(dataValidatorBuilder, loan, jsonElement, rescheduleFromDate, adjustedDueDate); + } + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } + + private void validateForProgressiveLoan(final DataValidatorBuilder dataValidatorBuilder, final Loan loan, final JsonElement jsonElement, + final LocalDate rescheduleFromDate, final LocalDate adjustedDueDate) { + final var unsupportedFields = List.of(RescheduleLoansApiConstants.graceOnPrincipalParamName, // + RescheduleLoansApiConstants.graceOnInterestParamName, // + RescheduleLoansApiConstants.extraTermsParamName, // + RescheduleLoansApiConstants.emiParamName// + ); + + for (var unsupportedField : unsupportedFields) { + if (this.fromJsonHelper.parameterHasValue(unsupportedField, jsonElement)) { + dataValidatorBuilder.reset().parameter(unsupportedField).failWithCode( + RescheduleLoansApiConstants.rescheduleSelectedOperationNotSupportedErrorCode, + "Selected operation is not supported by Progressive Loan at a time during Loan Rescheduling"); + return; + } + } + + final LocalDate businessDate = DateUtils.getBusinessLocalDate(); + LoanRepaymentScheduleInstallment installment = null; + if (rescheduleFromDate != null) { + boolean hasInterestRateChange = this.fromJsonHelper.parameterHasValue(RescheduleLoansApiConstants.newInterestRateParamName, + jsonElement); + if (hasInterestRateChange && adjustedDueDate != null) { + dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.adjustedDueDateParamName).failWithCode( + RescheduleLoansApiConstants.rescheduleMultipleOperationsNotSupportedErrorCode, + "Only one operation is supported at a time during Loan Rescheduling"); + return; + } + + if (hasInterestRateChange && !rescheduleFromDate.isAfter(businessDate)) { + dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName).failWithCode( + "loan.reschedule.interestratechange.reschedulefrom.shouldbefuture", + "Loan Reschedule From date should be in the future."); + } + + if (adjustedDueDate != null) { + installment = loan.getRepaymentScheduleInstallment(rescheduleFromDate); + } else if (hasInterestRateChange) { + installment = loan.getRelatedRepaymentScheduleInstallment(rescheduleFromDate); + } + + validateReschedulingInstallment(dataValidatorBuilder, installment); + } + + validateForOverdueCharges(dataValidatorBuilder, loan, installment); + } + + private void validateForCumulativeLoan(final DataValidatorBuilder dataValidatorBuilder, final Loan loan, final JsonElement jsonElement, + final LocalDate rescheduleFromDate, final LocalDate endDate, final BigDecimal emi) { + if (emi != null || endDate != null) { dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.endDateParamName).value(endDate).notNull(); dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.emiParamName).value(emi).notNull().positiveAmount(); @@ -156,15 +223,6 @@ public class LoanRescheduleRequestDataValidator { .failWithCode("repayment.schedule.installment.does.not.exist", "Repayment schedule installment does not exist"); } } - - } - - final LocalDate adjustedDueDate = this.fromJsonHelper.extractLocalDateNamed(RescheduleLoansApiConstants.adjustedDueDateParamName, - jsonElement); - - if (adjustedDueDate != null && DateUtils.isBefore(adjustedDueDate, rescheduleFromDate)) { - dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName).failWithCode( - "adjustedDueDate.before.rescheduleFromDate", "Adjusted due date cannot be before the reschedule from date"); } // at least one of the following must be provided => graceOnPrincipal, @@ -177,37 +235,38 @@ public class LoanRescheduleRequestDataValidator { && !this.fromJsonHelper.parameterExists(RescheduleLoansApiConstants.emiParamName, jsonElement)) { dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.graceOnPrincipalParamName).notNull(); } - LoanRepaymentScheduleInstallment installment = null; + + final LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(rescheduleFromDate); + if (rescheduleFromDate != null) { - installment = loan.getRepaymentScheduleInstallment(rescheduleFromDate); - - if (installment == null) { - dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) - .failWithCode("repayment.schedule.installment.does.not.exist", "Repayment schedule installment does not exist"); - } - - if (installment != null && installment.isObligationsMet()) { - dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) - .failWithCode("repayment.schedule.installment.obligation.met", "Repayment schedule installment obligation met"); - } - + validateReschedulingInstallment(dataValidatorBuilder, installment); } if (loan.isMultiDisburmentLoan()) { if (!loan.loanProduct().isDisallowExpectedDisbursements()) { dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode( - RescheduleLoansApiConstants.resheduleForMultiDisbursementNotSupportedErrorCode, + RescheduleLoansApiConstants.rescheduleForMultiDisbursementNotSupportedErrorCode, "Loan rescheduling is not supported for multidisbursement tranche loans"); } } validateForOverdueCharges(dataValidatorBuilder, loan, installment); - if (!dataValidationErrors.isEmpty()) { - throw new PlatformApiDataValidationException(dataValidationErrors); + } + + private static void validateReschedulingInstallment(DataValidatorBuilder dataValidatorBuilder, + LoanRepaymentScheduleInstallment installment) { + if (installment == null) { + dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) + .failWithCode("repayment.schedule.installment.does.not.exist", "Repayment schedule installment does not exist"); + } + + if (installment != null && installment.isObligationsMet()) { + dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName) + .failWithCode("repayment.schedule.installment.obligation.met", "Repayment schedule installment obligation met"); } } - private void validateForOverdueCharges(DataValidatorBuilder dataValidatorBuilder, final Loan loan, + private void validateForOverdueCharges(final DataValidatorBuilder dataValidatorBuilder, final Loan loan, final LoanRepaymentScheduleInstallment installment) { if (installment != null) { LocalDate rescheduleFromDate = installment.getFromDate(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java index 266514dac..751c3d41f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java @@ -195,29 +195,18 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche submittedOnDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.submittedOnDateParamName); } - // initially set the value to null - LocalDate rescheduleFromDate = null; - // start point of the rescheduling exercise Integer rescheduleFromInstallment = null; // initially set the value to null LocalDate adjustedDueDate = null; + LocalDate rescheduleFromDate = jsonCommand + .localDateValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleFromDateParamName); // check if the parameter is in the JsonCommand object - if (jsonCommand.hasParameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)) { - // create a LocalDate object from the "rescheduleFromDate" Date - // string - LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleFromDateParamName); - - if (localDate != null) { - // get installment by due date - LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(localDate); - rescheduleFromInstallment = installment.getInstallmentNumber(); - - // update the value of the "rescheduleFromDate" variable - rescheduleFromDate = localDate; - } + if (rescheduleFromDate != null) { + // get installment by due date + rescheduleFromInstallment = loan.getRelatedRepaymentScheduleInstallment(rescheduleFromDate).getInstallmentNumber(); } if (jsonCommand.hasParameter(RescheduleLoansApiConstants.adjustedDueDateParamName)) {