FINERACT-1806: Add charge-off reasons to loan product response

This commit is contained in:
Oleksii Novikov
2024-11-14 15:10:42 +01:00
committed by Adam Saghy
parent 6286d1f821
commit 628f27bf32
11 changed files with 197 additions and 5 deletions
@@ -53,6 +53,7 @@ import org.apache.fineract.client.services.SchedulerApi;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.client.util.FineractClient;
import org.apache.fineract.test.stepdef.loan.LoanProductsCustomApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -98,6 +99,11 @@ public class ApiConfiguration {
return fineractClient.createService(LoanProductsApi.class);
}
@Bean
public LoanProductsCustomApi loanProductsCustomApi() {
return fineractClient.createService(LoanProductsCustomApi.class);
}
@Bean
public SavingsProductApi savingsProductApi() {
return fineractClient.createService(SavingsProductApi.class);
@@ -24,6 +24,7 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.client.models.BatchResponse;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
@@ -888,4 +889,19 @@ public final class ErrorMessageHelper {
public static String downpaymentDisabledOnProductErrorCodeMsg() {
return "The Loan can not override the downpayment properties because in the Loan Product the downpayment is disabled";
}
public static String wrongValueInLineInChargeOffReasonOptions(final int line, final List<List<String>> actual,
final List<String> expected) {
final String actualValues = actual.stream().map(List::toString).collect(Collectors.joining(System.lineSeparator()));
return String.format(
"%nWrong value in Loan Charge-Off Reason Options line %s. %nActual values in line: %s %nExpected values in line: %s", line,
actualValues, expected);
}
public static String wrongNumberOfLinesInChargeOffReasonOptions(final int actual, final int expected) {
return String.format(
"Number of lines in loan charge-off reason options is not correct. Actual value is: %d - Expected value is: %d", actual,
expected);
}
}
@@ -0,0 +1,30 @@
/**
* 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.test.stepdef.loan;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import retrofit2.Call;
import retrofit2.http.GET;
public interface LoanProductsCustomApi {
@GET("v1/loanproducts/{productId}")
Call<GetLoanProductsProductIdResponse> retrieveLoanProductDetails(@retrofit2.http.Path("productId") Long productId,
@retrofit2.http.Query("template") String isTemplate);
}
@@ -52,7 +52,9 @@ import org.apache.fineract.avro.loan.v1.LoanStatusEnumDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.DeleteLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoanProductsChargeOffReasonOptions;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoanProductsTemplateResponse;
import org.apache.fineract.client.models.GetLoansLoanIdDelinquencySummary;
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargePaidByData;
@@ -158,6 +160,9 @@ public class LoanStepDef extends AbstractStepDef {
@Autowired
private LoanProductsApi loanProductsApi;
@Autowired
private LoanProductsCustomApi loanProductsCustomApi;
@Autowired
private EventStore eventStore;
@@ -2550,6 +2555,66 @@ public class LoanStepDef extends AbstractStepDef {
assertTrue(relationshipOptional.isPresent(), "Missed relationship between transactions");
}
@Then("Loan Product Charge-Off reasons options from loan product template have {int} options, with the following data:")
public void loanProductTemplateChargeOffReasonOptionsCheck(final int linesExpected, final DataTable table) throws IOException {
final Response<GetLoanProductsTemplateResponse> loanProductDetails = loanProductsApi.retrieveTemplate11(false).execute();
ErrorHelper.checkSuccessfulApiCall(loanProductDetails);
assertNotNull(loanProductDetails.body());
final List<GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions = loanProductDetails.body().getChargeOffReasonOptions();
assertNotNull(chargeOffReasonOptions);
final List<List<String>> data = table.asLists();
final int linesActual = chargeOffReasonOptions.size();
data.stream().skip(1) // skip headers
.forEach(expectedValues -> {
final List<List<String>> actualValuesList = chargeOffReasonOptions.stream()
.map(chargeOffReason -> fetchValuesOfLoanChargeOffReasonOptions(data.get(0), chargeOffReason))
.collect(Collectors.toList());
final boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> actualValues.equals(expectedValues));
assertThat(containsExpectedValues).as(ErrorMessageHelper
.wrongValueInLineInChargeOffReasonOptions(data.indexOf(expectedValues), actualValuesList, expectedValues))
.isTrue();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInChargeOffReasonOptions(linesActual, linesExpected))
.isEqualTo(linesExpected);
});
}
@Then("Loan Product {string} Charge-Off reasons options from specific loan product have {int} options, with the following data:")
public void specificLoanProductChargeOffReasonOptionsCheck(final String loanProductName, final int linesExpected, final DataTable table)
throws IOException {
final DefaultLoanProduct product = DefaultLoanProduct.valueOf(loanProductName);
final Long loanProductId = loanProductResolver.resolve(product);
final Response<GetLoanProductsProductIdResponse> loanProductDetails = loanProductsCustomApi
.retrieveLoanProductDetails(loanProductId, "true").execute();
ErrorHelper.checkSuccessfulApiCall(loanProductDetails);
assertNotNull(loanProductDetails.body());
final List<GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions = loanProductDetails.body().getChargeOffReasonOptions();
assertNotNull(chargeOffReasonOptions);
final List<List<String>> data = table.asLists();
final int linesActual = chargeOffReasonOptions.size();
data.stream().skip(1) // skip headers
.forEach(expectedValues -> {
final List<List<String>> actualValuesList = chargeOffReasonOptions.stream()
.map(chargeOffReason -> fetchValuesOfLoanChargeOffReasonOptions(data.get(0), chargeOffReason))
.collect(Collectors.toList());
final boolean containsExpectedValues = actualValuesList.stream()
.anyMatch(actualValues -> actualValues.equals(expectedValues));
assertThat(containsExpectedValues).as(ErrorMessageHelper
.wrongValueInLineInChargeOffReasonOptions(data.indexOf(expectedValues), actualValuesList, expectedValues))
.isTrue();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInChargeOffReasonOptions(linesActual, linesExpected))
.isEqualTo(linesExpected);
});
}
private void createCustomizedLoan(final List<String> loanData, final boolean withEmi) throws IOException {
final String loanProduct = loanData.get(0);
final String submitDate = loanData.get(1);
@@ -2928,4 +2993,29 @@ public class LoanStepDef extends AbstractStepDef {
}
return actualValues;
}
@SuppressFBWarnings("SF_SWITCH_NO_DEFAULT")
private List<String> fetchValuesOfLoanChargeOffReasonOptions(final List<String> header,
final GetLoanProductsChargeOffReasonOptions chargeOffReasonOption) {
final List<String> actualValues = new ArrayList<>();
for (String headerName : header) {
switch (headerName) {
case "Charge-Off Reason Name" ->
actualValues.add(chargeOffReasonOption.getName() == null ? null : chargeOffReasonOption.getName());
case "Description" -> {
assertNotNull(chargeOffReasonOption.getDescription());
actualValues
.add(chargeOffReasonOption.getDescription().isEmpty() || chargeOffReasonOption.getDescription() == null ? null
: chargeOffReasonOption.getDescription());
}
case "Position" -> actualValues
.add(chargeOffReasonOption.getPosition() == null ? null : String.valueOf(chargeOffReasonOption.getPosition()));
case "Is Active" ->
actualValues.add(chargeOffReasonOption.getActive() == null ? null : String.valueOf(chargeOffReasonOption.getActive()));
case "Is Mandatory" -> actualValues
.add(chargeOffReasonOption.getMandatory() == null ? null : String.valueOf(chargeOffReasonOption.getMandatory()));
}
}
return actualValues;
}
}