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 be935cd55 [FINERACT-1760] Loan product external id
be935cd55 is described below

commit be935cd55b9f57c9e98f31fa876c64c8163892e7
Author: taskain7 <[email protected]>
AuthorDate: Thu Jul 13 12:31:37 2023 +0200

    [FINERACT-1760] Loan product external id
---
 .../infrastructure/core/api/JsonCommand.java       |  11 +++
 .../portfolio/loanproduct/domain/LoanProduct.java  |  14 +--
 .../loanproduct/api/LoanProductsApiResource.java   | 103 +++++++++++++++++----
 .../api/LoanProductsApiResourceSwagger.java        |   2 +
 .../loanproduct/domain/LoanProductRepository.java  |   3 +
 .../exception/LoanProductNotFoundException.java    |   5 +
 .../service/LoanProductReadPlatformService.java    |   4 +
 .../LoanProductReadPlatformServiceImpl.java        |  26 +++---
 .../LoanProductExternalIdTest.java                 |  76 +++++++++++++++
 .../common/loans/LoanProductHelper.java            |  11 +++
 .../common/loans/LoanProductTestBuilder.java       |   7 ++
 11 files changed, 222 insertions(+), 40 deletions(-)

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 49ebc937d..32175c5f7 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
@@ -35,6 +35,7 @@ import java.util.Map;
 import java.util.Set;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
 import 
org.apache.fineract.infrastructure.security.domain.BasicPasswordEncodablePlatformUser;
 import org.apache.fineract.infrastructure.security.domain.PlatformUser;
@@ -643,6 +644,16 @@ public final class JsonCommand {
         return isChanged;
     }
 
+    public boolean isChangeInExternalIdParameterNamed(final String 
parameterName, final ExternalId externalId) {
+        boolean isChanged = false;
+        if (parameterExists(parameterName)) {
+            final String workingValue = 
stringValueOfParameterNamed(parameterName);
+            String existingValue = externalId.getValue();
+            isChanged = differenceExists(existingValue, workingValue);
+        }
+        return isChanged;
+    }
+
     public String passwordValueOfParameterNamed(final String parameterName, 
final PlatformPasswordEncoder platformPasswordEncoder,
             final Long saltValue) {
         final String passwordPlainText = 
stringValueOfParameterNamed(parameterName);
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
index 9ec2258d7..5218cba42 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
@@ -48,6 +48,8 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.accounting.common.AccountingRuleType;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import 
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.charge.domain.Charge;
@@ -130,7 +132,7 @@ public class LoanProduct extends AbstractPersistableCustom {
     private LocalDate closeDate;
 
     @Column(name = "external_id", length = 100, unique = true)
-    private String externalId;
+    private ExternalId externalId;
 
     @OneToMany(cascade = CascadeType.ALL, mappedBy = "loanProduct", 
orphanRemoval = true, fetch = FetchType.EAGER)
     private Set<LoanProductBorrowerCycleVariations> borrowerCycleVariations = 
new HashSet<>();
@@ -290,7 +292,7 @@ public class LoanProduct extends AbstractPersistableCustom {
 
         final LocalDate startDate = 
command.localDateValueOfParameterNamed("startDate");
         final LocalDate closeDate = 
command.localDateValueOfParameterNamed("closeDate");
-        final String externalId = 
command.stringValueOfParameterNamedAllowingNull("externalId");
+        final ExternalId externalId = 
ExternalIdFactory.produce(command.stringValueOfParameterNamedAllowingNull("externalId"));
 
         final boolean useBorrowerCycle = command
                 
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME);
@@ -593,7 +595,7 @@ public class LoanProduct extends AbstractPersistableCustom {
             final Integer graceOnPrincipalPayment, final Integer 
recurringMoratoriumOnPrincipalPeriods,
             final Integer graceOnInterestPayment, final Integer 
graceOnInterestCharged, final AmortizationMethod amortizationMethod,
             final BigDecimal inArrearsTolerance, final List<Charge> charges, 
final AccountingRuleType accountingRuleType,
-            final boolean includeInBorrowerCycle, final LocalDate startDate, 
final LocalDate closeDate, final String externalId,
+            final boolean includeInBorrowerCycle, final LocalDate startDate, 
final LocalDate closeDate, final ExternalId externalId,
             final boolean useBorrowerCycle, final 
Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations,
             final boolean multiDisburseLoan, final Integer maxTrancheCount, 
final BigDecimal outstandingLoanBalance,
             final Integer graceOnArrearsAgeing, final Integer 
overdueDaysForNPA, final DaysInMonthType daysInMonthType,
@@ -964,8 +966,8 @@ public class LoanProduct extends AbstractPersistableCustom {
         }
 
         final String externalIdTypeParamName = "externalId";
-        if (command.isChangeInStringParameterNamed(externalIdTypeParamName, 
this.externalId)) {
-            final String newValue = 
command.stringValueOfParameterNamed(externalIdTypeParamName);
+        if 
(command.isChangeInExternalIdParameterNamed(externalIdTypeParamName, 
this.externalId)) {
+            final ExternalId newValue = 
ExternalIdFactory.produce(command.stringValueOfParameterNamed(externalIdTypeParamName));
             actualChanges.put(accountingTypeParamName, newValue);
             this.externalId = newValue;
         }
@@ -1315,7 +1317,7 @@ public class LoanProduct extends 
AbstractPersistableCustom {
         return this.name;
     }
 
-    public String getExternalId() {
+    public ExternalId getExternalId() {
         return this.externalId;
     }
 
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 c834a7aca..910b8069e 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
@@ -43,6 +43,7 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import lombok.RequiredArgsConstructor;
 import 
org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
@@ -58,8 +59,10 @@ import 
org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
 import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import 
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
 import 
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
 import 
org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
@@ -76,6 +79,7 @@ import 
org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
 import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
 import 
org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
+import 
org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
 import 
org.apache.fineract.portfolio.loanproduct.productmix.data.ProductMixData;
 import 
org.apache.fineract.portfolio.loanproduct.productmix.service.ProductMixReadPlatformService;
 import 
org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
@@ -230,6 +234,86 @@ public class LoanProductsApiResource {
 
         
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
 
+        return getLoanProductDetails(productId, uriInfo);
+    }
+
+    @PUT
+    @Path("{productId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update a Loan Product", description = "Updates a 
Loan Product")
+    @RequestBody(required = true, content = @Content(schema = 
@Schema(implementation = 
LoanProductsApiResourceSwagger.PutLoanProductsProductIdRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
LoanProductsApiResourceSwagger.PutLoanProductsProductIdResponse.class))) })
+    public String updateLoanProduct(@PathParam("productId") 
@Parameter(description = "productId") final Long productId,
+            @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+        return getUpdateLoanProductResult(apiRequestBodyAsJson, productId);
+    }
+
+    @GET
+    @Path("external-id/{externalProductId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Retrieve a Loan Product", description = "Retrieves a 
Loan Product\n\n" + "Example Requests:\n" + "\n"
+            + 
"loanproducts/external-id/2075e308-d4a8-44d9-8203-f5a947b8c2f4\n" + "\n" + "\n"
+            + 
"loanproducts/external-id/2075e308-d4a8-44d9-8203-f5a947b8c2f4?template=true\n" 
+ "\n" + "\n"
+            + 
"loanproducts/external-id/2075e308-d4a8-44d9-8203-f5a947b8c2f4?fields=name,description,numberOfRepayments")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
LoanProductsApiResourceSwagger.GetLoanProductsProductIdResponse.class))) })
+    public String retrieveLoanProductDetails(
+            @PathParam("externalProductId") @Parameter(description = 
"externalProductId") final String externalProductId,
+            @Context final UriInfo uriInfo) {
+
+        
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+
+        ExternalId externalId = ExternalIdFactory.produce(externalProductId);
+
+        Long productId = resolveProductId(externalId);
+        if (Objects.isNull(productId)) {
+            throw new LoanProductNotFoundException(externalId);
+        }
+
+        return getLoanProductDetails(productId, uriInfo);
+    }
+
+    @PUT
+    @Path("external-id/{externalProductId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Update a Loan Product", description = "Updates a 
Loan Product")
+    @RequestBody(required = true, content = @Content(schema = 
@Schema(implementation = 
LoanProductsApiResourceSwagger.PutLoanProductsProductIdRequest.class)))
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
LoanProductsApiResourceSwagger.PutLoanProductsProductIdResponse.class))) })
+    public String updateLoanProduct(
+            @PathParam("externalProductId") @Parameter(description = 
"externalProductId") final String externalProductId,
+            @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+        ExternalId externalId = ExternalIdFactory.produce(externalProductId);
+
+        Long productId = resolveProductId(externalId);
+
+        if (Objects.isNull(productId)) {
+            throw new LoanProductNotFoundException(externalId);
+        }
+
+        return getUpdateLoanProductResult(apiRequestBodyAsJson, productId);
+    }
+
+    private String getUpdateLoanProductResult(String apiRequestBodyAsJson, 
Long productId) {
+        final CommandWrapper commandRequest = new 
CommandWrapperBuilder().updateLoanProduct(productId).withJson(apiRequestBodyAsJson)
+                .build();
+
+        final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+        return this.toApiJsonSerializer.serialize(result);
+    }
+
+    private Long resolveProductId(ExternalId externalProductId) {
+        return 
loanProductReadPlatformService.retrieveLoanProductByExternalId(externalProductId).getId();
+    }
+
+    private String getLoanProductDetails(Long productId, UriInfo uriInfo) {
         final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
 
         LoanProductData loanProduct = 
this.loanProductReadPlatformService.retrieveLoanProduct(productId);
@@ -256,25 +340,6 @@ public class LoanProductsApiResource {
         return this.toApiJsonSerializer.serialize(settings, loanProduct, 
LOAN_PRODUCT_DATA_PARAMETERS);
     }
 
-    @PUT
-    @Path("{productId}")
-    @Consumes({ MediaType.APPLICATION_JSON })
-    @Produces({ MediaType.APPLICATION_JSON })
-    @Operation(summary = "Update a Loan Product", description = "Updates a 
Loan Product")
-    @RequestBody(required = true, content = @Content(schema = 
@Schema(implementation = 
LoanProductsApiResourceSwagger.PutLoanProductsProductIdRequest.class)))
-    @ApiResponses({
-            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
LoanProductsApiResourceSwagger.PutLoanProductsProductIdResponse.class))) })
-    public String updateLoanProduct(@PathParam("productId") 
@Parameter(description = "productId") final Long productId,
-            @Parameter(hidden = true) final String apiRequestBodyAsJson) {
-
-        final CommandWrapper commandRequest = new 
CommandWrapperBuilder().updateLoanProduct(productId).withJson(apiRequestBodyAsJson)
-                .build();
-
-        final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
-
-        return this.toApiJsonSerializer.serialize(result);
-    }
-
     private LoanProductData handleTemplate(final LoanProductData productData) {
 
         Collection<ChargeData> chargeOptions = 
this.chargeReadPlatformService.retrieveLoanApplicableFees();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index 2626ac977..26fad80e1 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -41,6 +41,8 @@ final class LoanProductsApiResourceSwagger {
 
         @Schema(example = "LP Accrual Accounting")
         public String name;
+        @Schema(example = "2075e308-d4a8-44d9-8203-f5a947b8c2f4")
+        public String externalId;
         @Schema(example = "LPAA")
         public String shortName;
         @Schema(example = "non-interest bearing product")
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRepository.java
index 64c93583d..68e912b04 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRepository.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRepository.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.portfolio.loanproduct.domain;
 
 import java.util.List;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -33,4 +34,6 @@ public interface LoanProductRepository extends 
JpaRepository<LoanProduct, Long>,
     Long countByDelinquencyBucket(DelinquencyBucket delinquencyBucket);
 
     List<LoanProduct> findByDelinquencyBucketNotNull();
+
+    LoanProduct findByExternalId(ExternalId externalId);
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/exception/LoanProductNotFoundException.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/exception/LoanProductNotFoundException.java
index 13b6ff69e..a4f1cbece 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/exception/LoanProductNotFoundException.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/exception/LoanProductNotFoundException.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.portfolio.loanproduct.exception;
 
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import 
org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
 import org.springframework.dao.EmptyResultDataAccessException;
 
@@ -30,6 +31,10 @@ public class LoanProductNotFoundException extends 
AbstractPlatformResourceNotFou
         super("error.msg.loanproduct.id.invalid", "Loan product with 
identifier " + id + " does not exist", id);
     }
 
+    public LoanProductNotFoundException(final ExternalId externalId) {
+        super("error.msg.loanproduct.id.invalid", "Loan product with 
identifier " + externalId + " does not exist", externalId);
+    }
+
     public LoanProductNotFoundException(Long id, 
EmptyResultDataAccessException e) {
         super("error.msg.loanproduct.id.invalid", "Loan product with 
identifier " + id + " does not exist", id, e);
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformService.java
index 54df64fc7..27613d700 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformService.java
@@ -19,8 +19,10 @@
 package org.apache.fineract.portfolio.loanproduct.service;
 
 import java.util.Collection;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import 
org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
+import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 
 public interface LoanProductReadPlatformService {
 
@@ -34,6 +36,8 @@ public interface LoanProductReadPlatformService {
 
     LoanProductData retrieveLoanProduct(Long productId);
 
+    LoanProduct retrieveLoanProductByExternalId(ExternalId externalId);
+
     LoanProductData retrieveNewLoanProductDetails();
 
     Collection<LoanProductData> retrieveAllLoanProductsForCurrency(String 
currencyCode);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index 7122139c6..01cd94103 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -24,8 +24,10 @@ import java.sql.SQLException;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Collection;
+import lombok.RequiredArgsConstructor;
 import org.apache.fineract.accounting.common.AccountingEnumerations;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import 
org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
@@ -42,19 +44,21 @@ import 
org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVa
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductGuaranteeData;
 import 
org.apache.fineract.portfolio.loanproduct.data.LoanProductInterestRecalculationData;
+import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductParamType;
+import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
 import 
org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
 import org.apache.fineract.portfolio.rate.data.RateData;
 import org.apache.fineract.portfolio.rate.service.RateReadService;
 import org.jetbrains.annotations.NotNull;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.EmptyResultDataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.stereotype.Service;
 
 @Service
+@RequiredArgsConstructor
 public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatformService {
 
     private final PlatformSecurityContext context;
@@ -64,20 +68,7 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
     private final DatabaseSpecificSQLGenerator sqlGenerator;
     private final FineractEntityAccessUtil fineractEntityAccessUtil;
     private final DelinquencyReadPlatformService 
delinquencyReadPlatformService;
-
-    @Autowired
-    public LoanProductReadPlatformServiceImpl(final PlatformSecurityContext 
context,
-            final ChargeReadPlatformService chargeReadPlatformService, final 
JdbcTemplate jdbcTemplate,
-            final FineractEntityAccessUtil fineractEntityAccessUtil, final 
RateReadService rateReadService,
-            final DelinquencyReadPlatformService 
delinquencyReadPlatformService, final DatabaseSpecificSQLGenerator 
sqlGenerator) {
-        this.context = context;
-        this.chargeReadPlatformService = chargeReadPlatformService;
-        this.jdbcTemplate = jdbcTemplate;
-        this.fineractEntityAccessUtil = fineractEntityAccessUtil;
-        this.rateReadService = rateReadService;
-        this.sqlGenerator = sqlGenerator;
-        this.delinquencyReadPlatformService = delinquencyReadPlatformService;
-    }
+    private final LoanProductRepository loanProductRepository;
 
     @Override
     public LoanProductData retrieveLoanProduct(final Long loanProductId) {
@@ -99,6 +90,11 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
         }
     }
 
+    @Override
+    public LoanProduct retrieveLoanProductByExternalId(final ExternalId 
externalId) {
+        return loanProductRepository.findByExternalId(externalId);
+    }
+
     @Override
     public Collection<LoanProductBorrowerCycleVariationData> 
retrieveLoanProductBorrowerCycleVariations(final Long loanProductId) {
         final LoanProductBorrowerCycleMapper rm = new 
LoanProductBorrowerCycleMapper();
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductExternalIdTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductExternalIdTest.java
new file mode 100644
index 000000000..03d327012
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductExternalIdTest.java
@@ -0,0 +1,76 @@
+/**
+ * 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.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.HashMap;
+import java.util.UUID;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
+import org.apache.fineract.client.models.PutLoanProductsProductIdResponse;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.loans.LoanProductHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class LoanProductExternalIdTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private LoanTransactionHelper loanTransactionHelper;
+    private LoanProductHelper loanProductHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.loanTransactionHelper = new LoanTransactionHelper(requestSpec, 
responseSpec);
+        this.loanProductHelper = new LoanProductHelper();
+    }
+
+    @Test
+    public void testLoanProductWithExternalId() {
+        String externalId = UUID.randomUUID().toString();
+        HashMap<String, Object> request = new 
LoanProductTestBuilder().withExternalId(externalId).build(null, null);
+        Integer loanProductId = 
loanTransactionHelper.getLoanProductId(Utils.convertToJson(request));
+        assertNotNull(loanProductId);
+
+        GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = 
loanProductHelper.retrieveLoanProductByExternalId(externalId);
+        assertNotNull(getLoanProductsProductIdResponse.getId());
+        assertEquals(loanProductId, 
getLoanProductsProductIdResponse.getId().intValue());
+
+        final PutLoanProductsProductIdRequest requestModifyLoan = new 
PutLoanProductsProductIdRequest()
+                .shortName(Utils.uniqueRandomStringGenerator("", 3));
+        PutLoanProductsProductIdResponse putLoanProductsProductIdResponse = 
loanProductHelper.updateLoanProductByExternalId(externalId,
+                requestModifyLoan);
+        assertNotNull(putLoanProductsProductIdResponse.getResourceId());
+        assertEquals(loanProductId, 
putLoanProductsProductIdResponse.getResourceId().intValue());
+    }
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
index d3f0a1be3..46c7aa1ff 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
@@ -18,8 +18,11 @@
  */
 package org.apache.fineract.integrationtests.common.loans;
 
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
 import org.apache.fineract.client.models.PostLoanProductsRequest;
 import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
+import org.apache.fineract.client.models.PutLoanProductsProductIdResponse;
 import org.apache.fineract.integrationtests.client.IntegrationTest;
 
 public class LoanProductHelper extends IntegrationTest {
@@ -29,4 +32,12 @@ public class LoanProductHelper extends IntegrationTest {
     public PostLoanProductsResponse createLoanProduct(PostLoanProductsRequest 
request) {
         return ok(fineract().loanProducts.createLoanProduct(request));
     }
+
+    public GetLoanProductsProductIdResponse 
retrieveLoanProductByExternalId(String externalId) {
+        return 
ok(fineract().loanProducts.retrieveLoanProductDetails1(externalId));
+    }
+
+    public PutLoanProductsProductIdResponse 
updateLoanProductByExternalId(String externalId, 
PutLoanProductsProductIdRequest request) {
+        return ok(fineract().loanProducts.updateLoanProduct1(externalId, 
request));
+    }
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
index a13b01e6c..49c6c9181 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -76,6 +76,7 @@ public class LoanProductTestBuilder {
 
     private String nameOfLoanProduct = 
Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
     private String shortName = Utils.uniqueRandomStringGenerator("", 4);
+    private String externalId = null;
     private String principal = "10000.00";
     private String numberOfRepayments = "5";
     private String repaymentFrequency = MONTHS;
@@ -160,6 +161,7 @@ public class LoanProductTestBuilder {
         }
         map.put("name", this.nameOfLoanProduct);
         map.put("shortName", this.shortName);
+        map.put("externalId", this.externalId);
         map.put("currencyCode", this.currencyCode);
         map.put("locale", LOCALE);
         map.put("dateFormat", "dd MMMM yyyy");
@@ -281,6 +283,11 @@ public class LoanProductTestBuilder {
         return map;
     }
 
+    public LoanProductTestBuilder withExternalId(String externalId) {
+        this.externalId = externalId;
+        return this;
+    }
+
     public LoanProductTestBuilder withInstallmentAmountInMultiplesOf(String 
installmentAmountInMultiplesOf) {
         this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         return this;

Reply via email to