FINERACT-2418: Loan Originator data with codevalues

This commit is contained in:
Jose Alberto Hernandez
2026-02-09 16:42:53 +01:00
committed by Adam Saghy
parent 99861ce93a
commit 28f63cc20e
10 changed files with 145 additions and 7 deletions
@@ -24,6 +24,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
@Data
@Builder
@@ -38,6 +39,6 @@ public class LoanOriginatorData implements Serializable {
private String externalId;
private String name;
private String status;
private Long originatorTypeId;
private Long channelTypeId;
private CodeValueData originatorType;
private CodeValueData channelType;
}
@@ -43,6 +43,7 @@ import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformS
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorTemplateData;
import org.apache.fineract.portfolio.loanorigination.service.LoanOriginatorReadPlatformService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@@ -83,6 +84,19 @@ public class LoanOriginatorApiResource {
return this.loanOriginatorReadPlatformService.retrieveAll();
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Get loan originator template data", description = "Retrieves the Loan Originator template data")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanOriginatorApiResourceSwagger.GetLoanOriginatorTemplateResponse.class)))
@ApiResponse(responseCode = "403", description = "Insufficient permissions")
public LoanOriginatorTemplateData retrieveLoanOriginatorTemplate() {
this.context.authenticatedUser().validateHasReadPermission(LoanOriginatorApiConstants.RESOURCE_NAME);
return this.loanOriginatorReadPlatformService.retrieveTemplate();
}
@GET
@Path("{originatorId}")
@Consumes({ MediaType.APPLICATION_JSON })
@@ -19,6 +19,9 @@
package org.apache.fineract.portfolio.loanorigination.api;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import java.util.Set;
import org.apache.fineract.infrastructure.codes.api.CodeValuesApiResourceSwagger.GetCodeValuesDataResponse;
final class LoanOriginatorApiResourceSwagger {
@@ -38,9 +41,21 @@ final class LoanOriginatorApiResourceSwagger {
@Schema(example = "ACTIVE")
public String status;
@Schema(example = "1")
public Long originatorTypeId;
public GetCodeValuesDataResponse originatorType;
@Schema(example = "1")
public Long channelTypeId;
public GetCodeValuesDataResponse channelType;
}
@Schema(description = "GetLoanOriginatorTemplateResponse")
public static final class GetLoanOriginatorTemplateResponse {
private GetLoanOriginatorTemplateResponse() {}
@Schema(example = "EXT-001")
public String externalId;
public Set<String> statusOptions;
public List<GetCodeValuesDataResponse> originatorTypeOptions;
public List<GetCodeValuesDataResponse> channelTypeOptions;
}
@Schema(description = "PostLoanOriginatorsRequest")
@@ -0,0 +1,35 @@
/**
* 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.portfolio.loanorigination.data;
import java.util.List;
import java.util.Set;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
@Data
@RequiredArgsConstructor
public class LoanOriginatorTemplateData {
private final String externalId;
private final Set<String> statusOptions;
private final List<CodeValueData> originatorTypeOptions;
private final List<CodeValueData> channelTypeOptions;
}
@@ -18,6 +18,9 @@
*/
package org.apache.fineract.portfolio.loanorigination.domain;
import java.util.HashSet;
import java.util.Set;
public enum LoanOriginatorStatus {
ACTIVE("ACTIVE"), PENDING("PENDING"), INACTIVE("INACTIVE");
@@ -28,10 +31,22 @@ public enum LoanOriginatorStatus {
this.value = value;
}
private static final Set<String> values = new HashSet<>();
static {
for (final LoanOriginatorStatus type : LoanOriginatorStatus.values()) {
values.add(type.value);
}
}
public String getValue() {
return value;
}
public static Set<String> getAllValues() {
return values;
}
public static LoanOriginatorStatus fromString(String text) {
for (LoanOriginatorStatus status : LoanOriginatorStatus.values()) {
if (status.value.equalsIgnoreCase(text)) {
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanorigination.mapper;
import java.util.List;
import org.apache.fineract.infrastructure.codes.mapper.CodeValueMapper;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginator;
@@ -26,12 +27,12 @@ import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Mapper(config = MapstructMapperConfig.class)
@Mapper(config = MapstructMapperConfig.class, uses = { CodeValueMapper.class })
@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled", havingValue = "true")
public interface LoanOriginatorMapper {
@Mapping(target = "originatorTypeId", source = "originatorType.id")
@Mapping(target = "channelTypeId", source = "channelType.id")
@Mapping(target = "originatorType", source = "originatorType")
@Mapping(target = "channelType", source = "channelType")
@Mapping(target = "externalId", source = "externalId")
@Mapping(target = "status", expression = "java(entity.getStatus().getValue())")
LoanOriginatorData toData(LoanOriginator entity);
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanorigination.service;
import java.util.List;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorTemplateData;
public interface LoanOriginatorReadPlatformService {
@@ -32,4 +33,6 @@ public interface LoanOriginatorReadPlatformService {
Long resolveIdByExternalId(String externalId);
List<LoanOriginatorData> retrieveByLoanId(Long loanId);
LoanOriginatorTemplateData retrieveTemplate();
}
@@ -21,12 +21,18 @@ package org.apache.fineract.portfolio.loanorigination.service;
import java.util.Collections;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.codes.mapper.CodeValueMapper;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorTemplateData;
import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginator;
import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMapping;
import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMappingRepository;
import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorRepository;
import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorStatus;
import org.apache.fineract.portfolio.loanorigination.exception.LoanOriginatorNotFoundException;
import org.apache.fineract.portfolio.loanorigination.mapper.LoanOriginatorMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -42,6 +48,8 @@ public class LoanOriginatorReadPlatformServiceImpl implements LoanOriginatorRead
private final LoanOriginatorRepository loanOriginatorRepository;
private final LoanOriginatorMappingRepository loanOriginatorMappingRepository;
private final LoanOriginatorMapper loanOriginatorMapper;
private final CodeValueRepository codeValueRepository;
private final CodeValueMapper codeValueMapper;
@Override
public List<LoanOriginatorData> retrieveAll() {
@@ -78,4 +86,14 @@ public class LoanOriginatorReadPlatformServiceImpl implements LoanOriginatorRead
}
return mappings.stream().map(LoanOriginatorMapping::getOriginator).map(this.loanOriginatorMapper::toData).toList();
}
@Override
public LoanOriginatorTemplateData retrieveTemplate() {
final List<CodeValueData> originationTypeOptions = codeValueMapper
.map(codeValueRepository.findByCodeName(LoanOriginatorApiConstants.ORIGINATOR_TYPE_CODE_NAME));
final List<CodeValueData> channelTypeOptions = codeValueMapper
.map(codeValueRepository.findByCodeName(LoanOriginatorApiConstants.CHANNEL_TYPE_CODE_NAME));
return new LoanOriginatorTemplateData(ExternalId.generate().getValue(), LoanOriginatorStatus.getAllValues(), originationTypeOptions,
channelTypeOptions);
}
}
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.UUID;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.GetLoanOriginatorTemplateResponse;
import org.apache.fineract.client.models.GetLoanOriginatorsResponse;
import org.apache.fineract.client.models.PostLoanOriginatorsRequest;
import org.apache.fineract.client.models.PostLoanOriginatorsResponse;
@@ -133,4 +134,8 @@ public class FeignLoanOriginatorHelper {
return null;
});
}
public GetLoanOriginatorTemplateResponse retrieveLoanOriginatorTemplate() {
return ok(() -> fineractClient.loanOriginators().retrieveLoanOriginatorTemplate());
}
}
@@ -19,8 +19,11 @@
package org.apache.fineract.integrationtests.client.feign.tests;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.GetCodeValuesDataResponse;
import org.apache.fineract.client.models.GetLoanOriginatorTemplateResponse;
import org.apache.fineract.client.models.GetLoanOriginatorsResponse;
import org.apache.fineract.client.models.PostLoanOriginatorsRequest;
import org.apache.fineract.client.models.PutLoanOriginatorsRequest;
@@ -30,10 +33,12 @@ import org.apache.fineract.integrationtests.client.feign.helpers.FeignClientHelp
import org.apache.fineract.integrationtests.client.feign.helpers.FeignLoanHelper;
import org.apache.fineract.integrationtests.client.feign.helpers.FeignLoanOriginatorHelper;
import org.apache.fineract.integrationtests.common.FineractFeignClientHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
@Slf4j
@Order(1)
public class FeignLoanOriginatorApiTest extends FeignIntegrationTest {
@@ -84,6 +89,32 @@ public class FeignLoanOriginatorApiTest extends FeignIntegrationTest {
originatorHelper.deleteOriginator(originatorId);
}
@Test
public void testCreateOriginatorWithAllFieldsUsingTemplate() {
final String name = Utils.randomStringGenerator("Originator ", 30);
final GetLoanOriginatorTemplateResponse originatorTemplate = originatorHelper.retrieveLoanOriginatorTemplate();
assertThat(originatorTemplate).isNotNull();
final String status = originatorTemplate.getStatusOptions().iterator().next();
final GetCodeValuesDataResponse originatorTypeCode = originatorTemplate.getOriginatorTypeOptions().get(0);
final GetCodeValuesDataResponse channelTypeCode = originatorTemplate.getChannelTypeOptions().get(0);
final Long originatorId = originatorHelper
.createOriginator(new PostLoanOriginatorsRequest().externalId(originatorTemplate.getExternalId()).name(name).status(status)
.originatorTypeId(originatorTypeCode.getId()).channelTypeId(channelTypeCode.getId()));
final GetLoanOriginatorsResponse originator = originatorHelper.getOriginatorById(originatorId);
assertThat(originator.getExternalId()).isEqualTo(originatorTemplate.getExternalId());
assertThat(originator.getName()).isEqualTo(name);
assertThat(originator.getStatus()).isEqualTo(status);
assertThat(originator.getOriginatorType().getName()).isEqualTo(originatorTypeCode.getName());
assertThat(originator.getChannelType().getName()).isEqualTo(channelTypeCode.getName());
originatorHelper.deleteOriginator(originatorId);
}
@Test
public void testRetrieveOriginatorById() {
final String externalId = FeignLoanOriginatorHelper.generateUniqueExternalId();