This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 628f27bf3 FINERACT-1806: Add charge-off reasons to loan product 
response
628f27bf3 is described below

commit 628f27bf322de146488511ba3fc69d1a4631a2b6
Author: Oleksii Novikov <[email protected]>
AuthorDate: Tue Nov 12 11:54:18 2024 +0200

    FINERACT-1806: Add charge-off reasons to loan product response
---
 .../service/CodeValueReadPlatformService.java      |  3 +-
 .../apache/fineract/test/api/ApiConfiguration.java |  6 ++
 .../fineract/test/helper/ErrorMessageHelper.java   | 16 ++++
 .../test/stepdef/loan/LoanProductsCustomApi.java   | 30 ++++++++
 .../fineract/test/stepdef/loan/LoanStepDef.java    | 90 ++++++++++++++++++++++
 .../test/resources/features/LoanProduct.feature    | 18 +++++
 .../api/LoanProductsApiResourceSwagger.java        | 20 +++++
 .../loanproduct/data/LoanProductData.java          |  7 +-
 .../BulkImportWorkbookPopulatorServiceImpl.java    |  2 +-
 .../service/CodeValueReadPlatformServiceImpl.java  |  3 +-
 .../loanproduct/api/LoanProductsApiResource.java   |  7 +-
 11 files changed, 197 insertions(+), 5 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformService.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformService.java
index d1e508bc4..f56a9ddc8 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformService.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformService.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.infrastructure.codes.service;
 
 import java.util.Collection;
+import java.util.List;
 import org.apache.fineract.infrastructure.codes.data.CodeValueData;
 
 /**
@@ -40,7 +41,7 @@ import 
org.apache.fineract.infrastructure.codes.data.CodeValueData;
  */
 public interface CodeValueReadPlatformService {
 
-    Collection<CodeValueData> retrieveCodeValuesByCode(String code);
+    List<CodeValueData> retrieveCodeValuesByCode(String code);
 
     Collection<CodeValueData> retrieveAllCodeValues(Long codeId);
 
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
index 9fd37335a..768b9166c 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/api/ApiConfiguration.java
@@ -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);
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
index 09b640610..03000bbea 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
@@ -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);
+    }
 }
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanProductsCustomApi.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanProductsCustomApi.java
new file mode 100644
index 000000000..6fd53a274
--- /dev/null
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanProductsCustomApi.java
@@ -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);
+}
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
index 5e1b2b783..6481dfb0a 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
@@ -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;
+    }
 }
diff --git 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanProduct.feature 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanProduct.feature
index 271f79471..c58cfc481 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanProduct.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanProduct.feature
@@ -181,3 +181,21 @@ Feature: LoanProduct
     Then Loan has 500 outstanding amount
     When Refund undo happens on "1 July 2022"
     Then Loan has 1000 outstanding amount
+
+  Scenario: As a user I would like to verify Charge-Off reasons options in 
loan product template response
+    When Admin sets the business date to "12 December 2021"
+    When Admin creates a client with random data
+    And Admin successfully creates a new customised Loan submitted on date: 
"12 December 2021", with Principal: "1000", a loanTermFrequency: 1 months, and 
numberOfRepayments: 1
+    Then Loan Product Charge-Off reasons options from loan product template 
have 2 options, with the following data:
+      | Charge-Off Reason Name | Description | Position | Is Active | Is 
Mandatory |
+      | debit_card             |             | 0        | true      | false    
    |
+      | credit_card            |             | 1        | true      | false    
    |
+
+  Scenario: As a user I would like to verify Charge-Off reasons options in 
specific loan product response
+    When Admin sets the business date to "12 December 2021"
+    When Admin creates a client with random data
+    And Admin successfully creates a new customised Loan submitted on date: 
"12 December 2021", with Principal: "1000", a loanTermFrequency: 1 months, and 
numberOfRepayments: 1
+    Then Loan Product "LP1" Charge-Off reasons options from specific loan 
product have 2 options, with the following data:
+      | Charge-Off Reason Name | Description | Position | Is Active | Is 
Mandatory |
+      | debit_card             |             | 0        | true      | false    
    |
+      | credit_card            |             | 1        | true      | false    
    |
\ No newline at end of file
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index cd04ef8e8..5ecfb6cff 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -1016,6 +1016,24 @@ final class LoanProductsApiResourceSwagger {
             public String description;
         }
 
+        static final class GetLoanProductsChargeOffReasonOptions {
+
+            private GetLoanProductsChargeOffReasonOptions() {}
+
+            @Schema(example = "2")
+            public Long id;
+            @Schema(example = "debit_card")
+            public String name;
+            @Schema(example = "2")
+            public Integer position;
+            @Schema(example = "Charge-Off reason description")
+            public String description;
+            @Schema(example = "true")
+            public Boolean active;
+            @Schema(example = "false")
+            public Boolean mandatory;
+        }
+
         @Schema(example = "false")
         public Boolean includeInBorrowerCycle;
         @Schema(example = "false")
@@ -1068,6 +1086,7 @@ final class LoanProductsApiResourceSwagger {
 
         public List<StringEnumOptionData> supportedInterestRefundTypes;
         public List<StringEnumOptionData> supportedInterestRefundTypesOptions;
+        public List<GetLoanProductsChargeOffReasonOptions> 
chargeOffReasonOptions;
     }
 
     @Schema(description = "GetLoanProductsProductIdResponse")
@@ -1342,6 +1361,7 @@ final class LoanProductsApiResourceSwagger {
         @Schema(example = "false")
         public Boolean enableAccrualActivityPosting;
         public List<StringEnumOptionData> supportedInterestRefundTypes;
+        public 
List<GetLoanProductsTemplateResponse.GetLoanProductsChargeOffReasonOptions> 
chargeOffReasonOptions;
     }
 
     @Schema(description = "PutLoanProductsProductIdRequest")
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
index f1c232985..c32997cb6 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
@@ -32,6 +32,7 @@ import 
org.apache.fineract.accounting.common.AccountingRuleType;
 import org.apache.fineract.accounting.glaccount.data.GLAccountData;
 import 
org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
 import 
org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
+import org.apache.fineract.infrastructure.codes.data.CodeValueData;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
@@ -212,6 +213,8 @@ public class LoanProductData implements Serializable {
     private final boolean isEqualAmortization;
     private final BigDecimal fixedPrincipalPercentagePerInstallment;
 
+    private final List<CodeValueData> chargeOffReasonOptions;
+
     // Delinquency Buckets
     private final Collection<DelinquencyBucketData> delinquencyBucketOptions;
     private final DelinquencyBucketData delinquencyBucket;
@@ -913,6 +916,7 @@ public class LoanProductData implements Serializable {
         this.enableAccrualActivityPosting = enableAccrualActivityPosting;
         this.supportedInterestRefundTypes = supportedInterestRefundTypes;
         this.supportedInterestRefundTypesOptions = null;
+        this.chargeOffReasonOptions = null;
     }
 
     public LoanProductData(final LoanProductData productData, final 
Collection<ChargeData> chargeOptions,
@@ -935,7 +939,7 @@ public class LoanProductData implements Serializable {
             final List<EnumOptionData> advancedPaymentAllocationTypes, final 
List<EnumOptionData> loanScheduleTypeOptions,
             final List<EnumOptionData> loanScheduleProcessingTypeOptions, 
final List<EnumOptionData> creditAllocationTransactionTypes,
             final List<EnumOptionData> creditAllocationAllocationTypes,
-            final List<StringEnumOptionData> 
supportedInterestRefundTypesOptions) {
+            final List<StringEnumOptionData> 
supportedInterestRefundTypesOptions, final List<CodeValueData> 
chargeOffReasonOptions) {
 
         this.id = productData.id;
         this.name = productData.name;
@@ -1092,6 +1096,7 @@ public class LoanProductData implements Serializable {
         this.enableAccrualActivityPosting = 
productData.enableAccrualActivityPosting;
         this.supportedInterestRefundTypesOptions = 
supportedInterestRefundTypesOptions;
         this.supportedInterestRefundTypes = 
productData.supportedInterestRefundTypes;
+        this.chargeOffReasonOptions = chargeOffReasonOptions;
     }
 
     private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> 
charges) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/service/BulkImportWorkbookPopulatorServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/service/BulkImportWorkbookPopulatorServiceImpl.java
index 353f56a1c..4774daf5c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/service/BulkImportWorkbookPopulatorServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/service/BulkImportWorkbookPopulatorServiceImpl.java
@@ -300,7 +300,7 @@ public class BulkImportWorkbookPopulatorServiceImpl 
implements BulkImportWorkboo
     private List<CodeValueData> fetchCodeValuesByCodeName(String codeName) {
         List<CodeValueData> codeValues = null;
         if (codeName != null) {
-            codeValues = (List<CodeValueData>) 
codeValueReadPlatformService.retrieveCodeValuesByCode(codeName);
+            codeValues = 
codeValueReadPlatformService.retrieveCodeValuesByCode(codeName);
         } else {
             throw new NullPointerException();
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java
index 7821aa062..23a2e1306 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java
@@ -21,6 +21,7 @@ package org.apache.fineract.infrastructure.codes.service;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Collection;
+import java.util.List;
 import org.apache.fineract.infrastructure.codes.data.CodeValueData;
 import 
org.apache.fineract.infrastructure.codes.exception.CodeValueNotFoundException;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
@@ -66,7 +67,7 @@ public class CodeValueReadPlatformServiceImpl implements 
CodeValueReadPlatformSe
 
     @Override
     @Cacheable(value = "code_values", key = 
"T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#code+'cv')")
-    public Collection<CodeValueData> retrieveCodeValuesByCode(final String 
code) {
+    public List<CodeValueData> retrieveCodeValuesByCode(final String code) {
 
         this.context.authenticatedUser();
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
index 75d16770b..7adcbdefd 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
@@ -54,6 +54,8 @@ import 
org.apache.fineract.accounting.producttoaccountmapping.service.ProductToG
 import org.apache.fineract.commands.domain.CommandWrapper;
 import org.apache.fineract.commands.service.CommandWrapperBuilder;
 import 
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
+import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import 
org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
 import 
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
 import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
 import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
@@ -150,6 +152,7 @@ public class LoanProductsApiResource {
     private final RateReadService rateReadService;
     private final ConfigurationDomainService configurationDomainService;
     private final DelinquencyReadPlatformService 
delinquencyReadPlatformService;
+    private final CodeValueReadPlatformService codeValueReadPlatformService;
 
     @POST
     @Consumes({ MediaType.APPLICATION_JSON })
@@ -425,6 +428,8 @@ public class LoanProductsApiResource {
         final List<EnumOptionData> creditAllocationAllocationTypes = 
AllocationType.getValuesAsEnumOptionDataList();
         final List<StringEnumOptionData> supportedInterestRefundTypesOptions = 
LoanSupportedInterestRefundTypes
                 .getValuesAsStringEnumOptionDataList();
+        final List<CodeValueData> chargeOffReasonOptions = 
codeValueReadPlatformService
+                .retrieveCodeValuesByCode(LoanApiConstants.CHARGE_OFF_REASONS);
 
         return new LoanProductData(productData, chargeOptions, penaltyOptions, 
paymentTypeOptions, currencyOptions, amortizationTypeOptions,
                 interestTypeOptions, interestCalculationPeriodTypeOptions, 
repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
@@ -436,7 +441,7 @@ public class LoanProductsApiResource {
                 advancedPaymentAllocationTransactionTypes, 
advancedPaymentAllocationFutureInstallmentAllocationRules,
                 advancedPaymentAllocationTypes, 
LoanScheduleType.getValuesAsEnumOptionDataList(),
                 LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), 
creditAllocationTransactionTypes,
-                creditAllocationAllocationTypes, 
supportedInterestRefundTypesOptions);
+                creditAllocationAllocationTypes, 
supportedInterestRefundTypesOptions, chargeOffReasonOptions);
     }
 
 }

Reply via email to