This is an automated email from the ASF dual-hosted git repository.
arnold 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 82e6a237a6 FINERACT-2418: add origination api implementation
82e6a237a6 is described below
commit 82e6a237a61d821b9ffd80f082c042ba059a113f
Author: Attila Budai <[email protected]>
AuthorDate: Thu Jan 22 16:43:23 2026 +0100
FINERACT-2418: add origination api implementation
---
.../fineract/client/feign/FineractFeignClient.java | 5 +
.../api/LoanOriginatorApiResource.java | 23 +-
.../api/LoanOriginatorApiResourceSwagger.java | 104 ++++++++
.../domain/LoanOriginatorRepository.java | 11 +
.../LoanOriginatorCannotBeDeletedException.java} | 19 +-
...oanOriginatorDuplicateExternalIdException.java} | 19 +-
.../LoanOriginatorInvalidStatusException.java} | 22 +-
.../LoanOriginatorNotFoundException.java} | 21 +-
.../CreateLoanOriginatorCommandHandler.java} | 35 +--
.../DeleteLoanOriginatorCommandHandler.java} | 35 +--
.../UpdateLoanOriginatorCommandHandler.java} | 38 ++-
.../LoanOriginatorMapper.java} | 37 +--
.../serialization/LoanOriginatorDataValidator.java | 121 +++++++++
.../LoanOriginatorReadPlatformServiceImpl.java | 33 ++-
.../LoanOriginatorWritePlatformServiceImpl.java | 154 ++++++++++++
.../loanorigination/module-changelog-master.xml | 1 +
.../loanorigination/parts/0002_permissions.xml | 78 ++++++
.../src/main/resources/application.properties | 2 +-
.../src/test/resources/application-test.properties | 3 +-
.../integrationtests/LoanOriginatorApiTest.java | 280 +++++++++++++++++++++
.../feign/helpers/FeignLoanOriginatorHelper.java | 108 ++++++++
.../feign/tests/FeignLoanOriginatorApiTest.java | 248 ++++++++++++++++++
.../common/loans/LoanOriginatorHelper.java | 166 ++++++++++++
23 files changed, 1408 insertions(+), 155 deletions(-)
diff --git
a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractFeignClient.java
b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractFeignClient.java
index 8c049039f8..61a3108f5e 100644
---
a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractFeignClient.java
+++
b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractFeignClient.java
@@ -96,6 +96,7 @@ import
org.apache.fineract.client.feign.services.LoanCollateralApi;
import org.apache.fineract.client.feign.services.LoanCollateralManagementApi;
import org.apache.fineract.client.feign.services.LoanDisbursementDetailsApi;
import org.apache.fineract.client.feign.services.LoanInterestPauseApi;
+import org.apache.fineract.client.feign.services.LoanOriginatorsApi;
import org.apache.fineract.client.feign.services.LoanProductsApi;
import org.apache.fineract.client.feign.services.LoanReschedulingApi;
import org.apache.fineract.client.feign.services.LoanTransactionsApi;
@@ -532,6 +533,10 @@ public final class FineractFeignClient {
return create(LoanInterestPauseApi.class);
}
+ public LoanOriginatorsApi loanOriginators() {
+ return create(LoanOriginatorsApi.class);
+ }
+
public LoanProductsApi loanProducts() {
return create(LoanProductsApi.class);
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/api/LoanOriginatorApiResource.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/api/LoanOriginatorApiResource.java
index 9053c4a1fe..7a437755d1 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/api/LoanOriginatorApiResource.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/api/LoanOriginatorApiResource.java
@@ -20,6 +20,10 @@ package org.apache.fineract.portfolio.loanorigination.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
@@ -58,7 +62,8 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a new loan originator", description =
"Creates a new loan originator record. Requires CREATE_LOAN_ORIGINATOR
permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanOriginatorApiResourceSwagger.PostLoanOriginatorsRequest.class)))
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.PostLoanOriginatorsResponse.class)))
@ApiResponse(responseCode = "400", description = "Required parameter is
missing or incorrect format")
@ApiResponse(responseCode = "403", description = "Duplicate external ID or
insufficient permissions")
public CommandProcessingResult create(@Parameter(hidden = true) final
String apiRequestBodyAsJson) {
@@ -70,7 +75,7 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List all loan originators", description = "Retrieves
all loan originator records. Requires READ_LOAN_ORIGINATOR permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(array = @ArraySchema(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.GetLoanOriginatorsResponse.class))))
@ApiResponse(responseCode = "403", description = "Insufficient
permissions")
public List<LoanOriginatorData> retrieveAll() {
this.context.authenticatedUser().validateHasReadPermission(LoanOriginatorApiConstants.RESOURCE_NAME);
@@ -83,7 +88,7 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a loan originator by ID", description =
"Retrieves a loan originator by its internal ID. Requires READ_LOAN_ORIGINATOR
permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.GetLoanOriginatorsResponse.class)))
@ApiResponse(responseCode = "403", description = "Insufficient
permissions")
@ApiResponse(responseCode = "404", description = "Originator not found")
public LoanOriginatorData retrieveOne(@PathParam("originatorId")
@Parameter(description = "originatorId") final Long originatorId) {
@@ -97,7 +102,7 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a loan originator by external ID",
description = "Retrieves a loan originator by its external ID. Requires
READ_LOAN_ORIGINATOR permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.GetLoanOriginatorsResponse.class)))
@ApiResponse(responseCode = "403", description = "Insufficient
permissions")
@ApiResponse(responseCode = "404", description = "Originator not found")
public LoanOriginatorData retrieveByExternalId(
@@ -112,7 +117,8 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a loan originator by ID", description =
"Updates a loan originator by its internal ID. Requires UPDATE_LOAN_ORIGINATOR
permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanOriginatorApiResourceSwagger.PutLoanOriginatorsRequest.class)))
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.PutLoanOriginatorsResponse.class)))
@ApiResponse(responseCode = "400", description = "Incorrect format")
@ApiResponse(responseCode = "403", description = "Insufficient
permissions")
@ApiResponse(responseCode = "404", description = "Originator not found")
@@ -128,7 +134,8 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a loan originator by external ID",
description = "Updates a loan originator by its external ID. Requires
UPDATE_LOAN_ORIGINATOR permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
LoanOriginatorApiResourceSwagger.PutLoanOriginatorsRequest.class)))
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.PutLoanOriginatorsResponse.class)))
@ApiResponse(responseCode = "400", description = "Incorrect format")
@ApiResponse(responseCode = "403", description = "Insufficient
permissions")
@ApiResponse(responseCode = "404", description = "Originator not found")
@@ -147,7 +154,7 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Delete a loan originator by ID", description =
"Deletes a loan originator by its internal ID. Requires DELETE_LOAN_ORIGINATOR
permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.DeleteLoanOriginatorsResponse.class)))
@ApiResponse(responseCode = "403", description = "Originator is mapped to
loans and cannot be deleted, or insufficient permissions")
@ApiResponse(responseCode = "404", description = "Originator not found")
public CommandProcessingResult delete(@PathParam("originatorId")
@Parameter(description = "originatorId") final Long originatorId) {
@@ -160,7 +167,7 @@ public class LoanOriginatorApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Delete a loan originator by external ID",
description = "Deletes a loan originator by its external ID. Requires
DELETE_LOAN_ORIGINATOR permission.")
- @ApiResponse(responseCode = "200", description = "OK")
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
LoanOriginatorApiResourceSwagger.DeleteLoanOriginatorsResponse.class)))
@ApiResponse(responseCode = "403", description = "Originator is mapped to
loans and cannot be deleted, or insufficient permissions")
@ApiResponse(responseCode = "404", description = "Originator not found")
public CommandProcessingResult deleteByExternalId(
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/api/LoanOriginatorApiResourceSwagger.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/api/LoanOriginatorApiResourceSwagger.java
new file mode 100644
index 0000000000..c80b0d281e
--- /dev/null
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/api/LoanOriginatorApiResourceSwagger.java
@@ -0,0 +1,104 @@
+/**
+ * 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.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+final class LoanOriginatorApiResourceSwagger {
+
+ private LoanOriginatorApiResourceSwagger() {}
+
+ @Schema(description = "GetLoanOriginatorsResponse")
+ public static final class GetLoanOriginatorsResponse {
+
+ private GetLoanOriginatorsResponse() {}
+
+ @Schema(example = "1")
+ public Long id;
+ @Schema(example = "EXT-001")
+ public String externalId;
+ @Schema(example = "Main Branch Originator")
+ public String name;
+ @Schema(example = "ACTIVE")
+ public String status;
+ @Schema(example = "1")
+ public Long originatorTypeId;
+ @Schema(example = "1")
+ public Long channelTypeId;
+ }
+
+ @Schema(description = "PostLoanOriginatorsRequest")
+ public static final class PostLoanOriginatorsRequest {
+
+ private PostLoanOriginatorsRequest() {}
+
+ @Schema(example = "EXT-001")
+ public String externalId;
+ @Schema(example = "Main Branch Originator", requiredMode =
Schema.RequiredMode.REQUIRED)
+ public String name;
+ @Schema(example = "ACTIVE")
+ public String status;
+ @Schema(example = "1")
+ public Long originatorTypeId;
+ @Schema(example = "1")
+ public Long channelTypeId;
+ }
+
+ @Schema(description = "PostLoanOriginatorsResponse")
+ public static final class PostLoanOriginatorsResponse {
+
+ private PostLoanOriginatorsResponse() {}
+
+ @Schema(example = "1")
+ public Long resourceId;
+ }
+
+ @Schema(description = "PutLoanOriginatorsRequest")
+ public static final class PutLoanOriginatorsRequest {
+
+ private PutLoanOriginatorsRequest() {}
+
+ @Schema(example = "Updated Name")
+ public String name;
+ @Schema(example = "INACTIVE")
+ public String status;
+ @Schema(example = "2")
+ public Long originatorTypeId;
+ @Schema(example = "2")
+ public Long channelTypeId;
+ }
+
+ @Schema(description = "PutLoanOriginatorsResponse")
+ public static final class PutLoanOriginatorsResponse {
+
+ private PutLoanOriginatorsResponse() {}
+
+ @Schema(example = "1")
+ public Long resourceId;
+ }
+
+ @Schema(description = "DeleteLoanOriginatorsResponse")
+ public static final class DeleteLoanOriginatorsResponse {
+
+ private DeleteLoanOriginatorsResponse() {}
+
+ @Schema(example = "1")
+ public Long resourceId;
+ }
+}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
index 09c2969240..99bf4a5c1b 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
@@ -23,6 +23,8 @@ import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
public interface LoanOriginatorRepository extends
JpaRepository<LoanOriginator, Long>, JpaSpecificationExecutor<LoanOriginator> {
@@ -31,4 +33,13 @@ public interface LoanOriginatorRepository extends
JpaRepository<LoanOriginator,
boolean existsByExternalId(ExternalId externalId);
List<LoanOriginator> findByStatus(LoanOriginatorStatus status);
+
+ @Query("SELECT lo FROM LoanOriginator lo LEFT JOIN FETCH lo.originatorType
LEFT JOIN FETCH lo.channelType")
+ List<LoanOriginator> findAllWithCodeValues();
+
+ @Query("SELECT lo FROM LoanOriginator lo LEFT JOIN FETCH lo.originatorType
LEFT JOIN FETCH lo.channelType WHERE lo.id = :id")
+ Optional<LoanOriginator> findByIdWithCodeValues(@Param("id") Long id);
+
+ @Query("SELECT lo FROM LoanOriginator lo LEFT JOIN FETCH lo.originatorType
LEFT JOIN FETCH lo.channelType WHERE lo.externalId = :externalId")
+ Optional<LoanOriginator>
findByExternalIdWithCodeValues(@Param("externalId") ExternalId externalId);
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorCannotBeDeletedException.java
similarity index 55%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorCannotBeDeletedException.java
index 09c2969240..a5c6fe265e 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorCannotBeDeletedException.java
@@ -16,19 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.domain;
+package org.apache.fineract.portfolio.loanorigination.exception;
-import java.util.List;
-import java.util.Optional;
-import org.apache.fineract.infrastructure.core.domain.ExternalId;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import
org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
-public interface LoanOriginatorRepository extends
JpaRepository<LoanOriginator, Long>, JpaSpecificationExecutor<LoanOriginator> {
+public class LoanOriginatorCannotBeDeletedException extends
AbstractPlatformDomainRuleException {
- Optional<LoanOriginator> findByExternalId(ExternalId externalId);
-
- boolean existsByExternalId(ExternalId externalId);
-
- List<LoanOriginator> findByStatus(LoanOriginatorStatus status);
+ public LoanOriginatorCannotBeDeletedException(Long id) {
+ super("error.msg.loan.originator.cannot.be.deleted.mapped.to.loan",
+ "Loan Originator with id " + id + " cannot be deleted as it is
mapped to one or more loans", id);
+ }
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorDuplicateExternalIdException.java
similarity index 55%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorDuplicateExternalIdException.java
index 09c2969240..33612c182b 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorDuplicateExternalIdException.java
@@ -16,19 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.domain;
+package org.apache.fineract.portfolio.loanorigination.exception;
-import java.util.List;
-import java.util.Optional;
-import org.apache.fineract.infrastructure.core.domain.ExternalId;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import
org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
-public interface LoanOriginatorRepository extends
JpaRepository<LoanOriginator, Long>, JpaSpecificationExecutor<LoanOriginator> {
+public class LoanOriginatorDuplicateExternalIdException extends
AbstractPlatformDomainRuleException {
- Optional<LoanOriginator> findByExternalId(ExternalId externalId);
-
- boolean existsByExternalId(ExternalId externalId);
-
- List<LoanOriginator> findByStatus(LoanOriginatorStatus status);
+ public LoanOriginatorDuplicateExternalIdException(String externalId) {
+ super("error.msg.loan.originator.duplicate.external.id", "Loan
Originator with external id '" + externalId + "' already exists",
+ externalId);
+ }
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorInvalidStatusException.java
similarity index 51%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorInvalidStatusException.java
index 09c2969240..3b8630dbf1 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorInvalidStatusException.java
@@ -16,19 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.domain;
+package org.apache.fineract.portfolio.loanorigination.exception;
-import java.util.List;
-import java.util.Optional;
-import org.apache.fineract.infrastructure.core.domain.ExternalId;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import
org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
-public interface LoanOriginatorRepository extends
JpaRepository<LoanOriginator, Long>, JpaSpecificationExecutor<LoanOriginator> {
+public class LoanOriginatorInvalidStatusException extends
AbstractPlatformDomainRuleException {
- Optional<LoanOriginator> findByExternalId(ExternalId externalId);
+ public LoanOriginatorInvalidStatusException(String status) {
+ super("error.msg.loan.originator.invalid.status",
+ "Invalid loan originator status: " + status + ". Valid values
are: ACTIVE, PENDING, INACTIVE", status);
+ }
- boolean existsByExternalId(ExternalId externalId);
-
- List<LoanOriginator> findByStatus(LoanOriginatorStatus status);
+ public LoanOriginatorInvalidStatusException(String status, Throwable
cause) {
+ super("error.msg.loan.originator.invalid.status",
+ "Invalid loan originator status: " + status + ". Valid values
are: ACTIVE, PENDING, INACTIVE", status, cause);
+ }
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorNotFoundException.java
similarity index 55%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorNotFoundException.java
index 09c2969240..eec740b206 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/domain/LoanOriginatorRepository.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/exception/LoanOriginatorNotFoundException.java
@@ -16,19 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.domain;
+package org.apache.fineract.portfolio.loanorigination.exception;
-import java.util.List;
-import java.util.Optional;
-import org.apache.fineract.infrastructure.core.domain.ExternalId;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import
org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
-public interface LoanOriginatorRepository extends
JpaRepository<LoanOriginator, Long>, JpaSpecificationExecutor<LoanOriginator> {
+public class LoanOriginatorNotFoundException extends
AbstractPlatformResourceNotFoundException {
- Optional<LoanOriginator> findByExternalId(ExternalId externalId);
+ public LoanOriginatorNotFoundException(Long id) {
+ super("error.msg.loan.originator.id.not.found", "Loan Originator with
id " + id + " not found", id);
+ }
- boolean existsByExternalId(ExternalId externalId);
-
- List<LoanOriginator> findByStatus(LoanOriginatorStatus status);
+ public LoanOriginatorNotFoundException(String externalId) {
+ super("error.msg.loan.originator.external.id.not.found", "Loan
Originator with external id " + externalId + " not found",
+ externalId);
+ }
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/CreateLoanOriginatorCommandHandler.java
similarity index 53%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/CreateLoanOriginatorCommandHandler.java
index dd9f84c3c6..cd8fe7196d 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/CreateLoanOriginatorCommandHandler.java
@@ -16,36 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.service;
+package org.apache.fineract.portfolio.loanorigination.handler;
-import java.util.List;
-import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.portfolio.loanorigination.service.LoanOriginatorWritePlatformService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
@Service
+@CommandType(entity = "LOAN_ORIGINATOR", action = "CREATE")
+@RequiredArgsConstructor
@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled",
havingValue = "true")
-public class LoanOriginatorReadPlatformServiceImpl implements
LoanOriginatorReadPlatformService {
+public class CreateLoanOriginatorCommandHandler implements
NewCommandSourceHandler {
- private static final String NOT_IMPLEMENTED_MESSAGE = "Not implemented
yet";
+ private final LoanOriginatorWritePlatformService writePlatformService;
@Override
- public List<LoanOriginatorData> retrieveAll() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public LoanOriginatorData retrieveById(Long id) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public LoanOriginatorData retrieveByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public Long resolveIdByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+ return this.writePlatformService.create(command);
}
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/DeleteLoanOriginatorCommandHandler.java
similarity index 53%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/DeleteLoanOriginatorCommandHandler.java
index dd9f84c3c6..383a182864 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/DeleteLoanOriginatorCommandHandler.java
@@ -16,36 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.service;
+package org.apache.fineract.portfolio.loanorigination.handler;
-import java.util.List;
-import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.portfolio.loanorigination.service.LoanOriginatorWritePlatformService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
@Service
+@CommandType(entity = "LOAN_ORIGINATOR", action = "DELETE")
+@RequiredArgsConstructor
@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled",
havingValue = "true")
-public class LoanOriginatorReadPlatformServiceImpl implements
LoanOriginatorReadPlatformService {
+public class DeleteLoanOriginatorCommandHandler implements
NewCommandSourceHandler {
- private static final String NOT_IMPLEMENTED_MESSAGE = "Not implemented
yet";
+ private final LoanOriginatorWritePlatformService writePlatformService;
@Override
- public List<LoanOriginatorData> retrieveAll() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public LoanOriginatorData retrieveById(Long id) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public LoanOriginatorData retrieveByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public Long resolveIdByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+ return this.writePlatformService.delete(command.entityId());
}
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/UpdateLoanOriginatorCommandHandler.java
similarity index 52%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/UpdateLoanOriginatorCommandHandler.java
index dd9f84c3c6..712e6f6743 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/handler/UpdateLoanOriginatorCommandHandler.java
@@ -1,3 +1,4 @@
+
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -16,36 +17,29 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.service;
+package org.apache.fineract.portfolio.loanorigination.handler;
-import java.util.List;
-import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.portfolio.loanorigination.service.LoanOriginatorWritePlatformService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
@Service
+@CommandType(entity = "LOAN_ORIGINATOR", action = "UPDATE")
+@RequiredArgsConstructor
@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled",
havingValue = "true")
-public class LoanOriginatorReadPlatformServiceImpl implements
LoanOriginatorReadPlatformService {
-
- private static final String NOT_IMPLEMENTED_MESSAGE = "Not implemented
yet";
-
- @Override
- public List<LoanOriginatorData> retrieveAll() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
+public class UpdateLoanOriginatorCommandHandler implements
NewCommandSourceHandler {
- @Override
- public LoanOriginatorData retrieveById(Long id) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public LoanOriginatorData retrieveByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
+ private final LoanOriginatorWritePlatformService writePlatformService;
+ @Transactional
@Override
- public Long resolveIdByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
+ public CommandProcessingResult processCommand(final JsonCommand command) {
+ return this.writePlatformService.update(command.entityId(), command);
}
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/mapper/LoanOriginatorMapper.java
similarity index 54%
copy from
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
copy to
fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/mapper/LoanOriginatorMapper.java
index dd9f84c3c6..c891c6e0cb 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/mapper/LoanOriginatorMapper.java
@@ -16,36 +16,25 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.portfolio.loanorigination.service;
+package org.apache.fineract.portfolio.loanorigination.mapper;
import java.util.List;
+import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
+import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginator;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.stereotype.Service;
-@Service
+@Mapper(config = MapstructMapperConfig.class)
@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled",
havingValue = "true")
-public class LoanOriginatorReadPlatformServiceImpl implements
LoanOriginatorReadPlatformService {
+public interface LoanOriginatorMapper {
- private static final String NOT_IMPLEMENTED_MESSAGE = "Not implemented
yet";
+ @Mapping(target = "originatorTypeId", source = "originatorType.id")
+ @Mapping(target = "channelTypeId", source = "channelType.id")
+ @Mapping(target = "externalId", source = "externalId")
+ @Mapping(target = "status", expression =
"java(entity.getStatus().getValue())")
+ LoanOriginatorData toData(LoanOriginator entity);
- @Override
- public List<LoanOriginatorData> retrieveAll() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public LoanOriginatorData retrieveById(Long id) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public LoanOriginatorData retrieveByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
-
- @Override
- public Long resolveIdByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
- }
+ List<LoanOriginatorData> toDataList(List<LoanOriginator> entities);
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/serialization/LoanOriginatorDataValidator.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/serialization/LoanOriginatorDataValidator.java
new file mode 100644
index 0000000000..dba7eeb67a
--- /dev/null
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/serialization/LoanOriginatorDataValidator.java
@@ -0,0 +1,121 @@
+/**
+ * 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.serialization;
+
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.CREATE_REQUEST_PARAMS;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.EXTERNAL_ID_PARAM;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.NAME_PARAM;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.RESOURCE_NAME;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.STATUS_PARAM;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.UPDATE_REQUEST_PARAMS;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import
org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorStatus;
+import
org.apache.fineract.portfolio.loanorigination.exception.LoanOriginatorInvalidStatusException;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled",
havingValue = "true")
+public class LoanOriginatorDataValidator {
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ public void validateForCreate(final String json) {
+ if (StringUtils.isBlank(json)) {
+ throw new InvalidJsonException();
+ }
+
+ final Type typeOfMap = new TypeToken<Map<String, Object>>()
{}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
CREATE_REQUEST_PARAMS);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new
DataValidatorBuilder(dataValidationErrors).resource(RESOURCE_NAME);
+
+ final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+ final String externalId =
this.fromApiJsonHelper.extractStringNamed(EXTERNAL_ID_PARAM, element);
+
baseDataValidator.reset().parameter(EXTERNAL_ID_PARAM).value(externalId).notBlank().notExceedingLengthOf(100);
+
+ final String name =
this.fromApiJsonHelper.extractStringNamed(NAME_PARAM, element);
+
baseDataValidator.reset().parameter(NAME_PARAM).value(name).ignoreIfNull().notExceedingLengthOf(255);
+
+ if (this.fromApiJsonHelper.parameterExists(STATUS_PARAM, element)) {
+ final String status =
this.fromApiJsonHelper.extractStringNamed(STATUS_PARAM, element);
+
baseDataValidator.reset().parameter(STATUS_PARAM).value(status).notBlank();
+ validateStatus(status);
+ }
+
+ throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ }
+
+ public void validateForUpdate(final String json) {
+ if (StringUtils.isBlank(json)) {
+ throw new InvalidJsonException();
+ }
+
+ final Type typeOfMap = new TypeToken<Map<String, Object>>()
{}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
UPDATE_REQUEST_PARAMS);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new
DataValidatorBuilder(dataValidationErrors).resource(RESOURCE_NAME);
+
+ final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+ final String name =
this.fromApiJsonHelper.extractStringNamed(NAME_PARAM, element);
+
baseDataValidator.reset().parameter(NAME_PARAM).value(name).ignoreIfNull().notExceedingLengthOf(255);
+
+ if (this.fromApiJsonHelper.parameterExists(STATUS_PARAM, element)) {
+ final String status =
this.fromApiJsonHelper.extractStringNamed(STATUS_PARAM, element);
+
baseDataValidator.reset().parameter(STATUS_PARAM).value(status).notBlank();
+ validateStatus(status);
+ }
+
+ throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ }
+
+ private void validateStatus(final String status) {
+ if (status != null) {
+ try {
+ LoanOriginatorStatus.fromString(status);
+ } catch (IllegalArgumentException e) {
+ throw new LoanOriginatorInvalidStatusException(status, e);
+ }
+ }
+ }
+
+ private void throwExceptionIfValidationWarningsExist(final
List<ApiParameterError> dataValidationErrors) {
+ if (!dataValidationErrors.isEmpty()) {
+ throw new PlatformApiDataValidationException(dataValidationErrors);
+ }
+ }
+}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
index dd9f84c3c6..9913858760 100644
---
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorReadPlatformServiceImpl.java
@@ -19,33 +19,50 @@
package org.apache.fineract.portfolio.loanorigination.service;
import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
+import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginator;
+import
org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorRepository;
+import
org.apache.fineract.portfolio.loanorigination.exception.LoanOriginatorNotFoundException;
+import
org.apache.fineract.portfolio.loanorigination.mapper.LoanOriginatorMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled",
havingValue = "true")
public class LoanOriginatorReadPlatformServiceImpl implements
LoanOriginatorReadPlatformService {
- private static final String NOT_IMPLEMENTED_MESSAGE = "Not implemented
yet";
+ private final LoanOriginatorRepository loanOriginatorRepository;
+ private final LoanOriginatorMapper loanOriginatorMapper;
@Override
public List<LoanOriginatorData> retrieveAll() {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
+ final List<LoanOriginator> originators =
this.loanOriginatorRepository.findAllWithCodeValues();
+ return this.loanOriginatorMapper.toDataList(originators);
}
@Override
- public LoanOriginatorData retrieveById(Long id) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
+ public LoanOriginatorData retrieveById(final Long id) {
+ final LoanOriginator originator =
this.loanOriginatorRepository.findByIdWithCodeValues(id)
+ .orElseThrow(() -> new LoanOriginatorNotFoundException(id));
+ return this.loanOriginatorMapper.toData(originator);
}
@Override
- public LoanOriginatorData retrieveByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
+ public LoanOriginatorData retrieveByExternalId(final String externalId) {
+ final LoanOriginator originator =
this.loanOriginatorRepository.findByExternalIdWithCodeValues(new
ExternalId(externalId))
+ .orElseThrow(() -> new
LoanOriginatorNotFoundException(externalId));
+ return this.loanOriginatorMapper.toData(originator);
}
@Override
- public Long resolveIdByExternalId(String externalId) {
- throw new UnsupportedOperationException(NOT_IMPLEMENTED_MESSAGE);
+ public Long resolveIdByExternalId(final String externalId) {
+ final LoanOriginator originator =
this.loanOriginatorRepository.findByExternalId(new ExternalId(externalId))
+ .orElseThrow(() -> new
LoanOriginatorNotFoundException(externalId));
+ return originator.getId();
}
}
diff --git
a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorWritePlatformServiceImpl.java
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorWritePlatformServiceImpl.java
new file mode 100644
index 0000000000..04801ea596
--- /dev/null
+++
b/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/service/LoanOriginatorWritePlatformServiceImpl.java
@@ -0,0 +1,154 @@
+/**
+ * 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.service;
+
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.CHANNEL_TYPE_CODE_NAME;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.CHANNEL_TYPE_ID_PARAM;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.EXTERNAL_ID_PARAM;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.NAME_PARAM;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.ORIGINATOR_TYPE_CODE_NAME;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.ORIGINATOR_TYPE_ID_PARAM;
+import static
org.apache.fineract.portfolio.loanorigination.api.LoanOriginatorApiConstants.STATUS_PARAM;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import
org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.portfolio.loanorigination.domain.LoanOriginator;
+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.LoanOriginatorCannotBeDeletedException;
+import
org.apache.fineract.portfolio.loanorigination.exception.LoanOriginatorDuplicateExternalIdException;
+import
org.apache.fineract.portfolio.loanorigination.exception.LoanOriginatorNotFoundException;
+import
org.apache.fineract.portfolio.loanorigination.serialization.LoanOriginatorDataValidator;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@Transactional
+@ConditionalOnProperty(value = "fineract.module.loan-origination.enabled",
havingValue = "true")
+public class LoanOriginatorWritePlatformServiceImpl implements
LoanOriginatorWritePlatformService {
+
+ private final LoanOriginatorRepository loanOriginatorRepository;
+ private final LoanOriginatorMappingRepository
loanOriginatorMappingRepository;
+ private final LoanOriginatorDataValidator loanOriginatorDataValidator;
+ private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
+
+ @Override
+ public CommandProcessingResult create(final JsonCommand command) {
+ this.loanOriginatorDataValidator.validateForCreate(command.json());
+
+ final String externalIdValue =
command.stringValueOfParameterNamed(EXTERNAL_ID_PARAM);
+ final ExternalId externalId = new ExternalId(externalIdValue);
+
+ if (this.loanOriginatorRepository.existsByExternalId(externalId)) {
+ throw new
LoanOriginatorDuplicateExternalIdException(externalIdValue);
+ }
+
+ final String name = command.stringValueOfParameterNamed(NAME_PARAM);
+
+ final String statusValue =
command.stringValueOfParameterNamed(STATUS_PARAM);
+ final LoanOriginatorStatus status = (statusValue != null &&
!statusValue.isEmpty()) ? LoanOriginatorStatus.fromString(statusValue)
+ : LoanOriginatorStatus.ACTIVE;
+
+ final CodeValue originatorType = resolveCodeValue(command,
ORIGINATOR_TYPE_ID_PARAM, ORIGINATOR_TYPE_CODE_NAME);
+ final CodeValue channelType = resolveCodeValue(command,
CHANNEL_TYPE_ID_PARAM, CHANNEL_TYPE_CODE_NAME);
+
+ final LoanOriginator originator = LoanOriginator.create(externalId,
name, status, originatorType, channelType);
+ this.loanOriginatorRepository.saveAndFlush(originator);
+
+ return new
CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(originator.getId())
+ .withEntityExternalId(externalId).build();
+ }
+
+ @Override
+ public CommandProcessingResult update(final Long id, final JsonCommand
command) {
+ this.loanOriginatorDataValidator.validateForUpdate(command.json());
+
+ final LoanOriginator originator =
this.loanOriginatorRepository.findById(id)
+ .orElseThrow(() -> new LoanOriginatorNotFoundException(id));
+
+ final Map<String, Object> changes = new LinkedHashMap<>();
+
+ if (command.isChangeInStringParameterNamed(NAME_PARAM,
originator.getName())) {
+ final String newName =
command.stringValueOfParameterNamed(NAME_PARAM);
+ originator.setName(newName);
+ changes.put(NAME_PARAM, newName);
+ }
+
+ if (command.isChangeInStringParameterNamed(STATUS_PARAM,
originator.getStatus().getValue())) {
+ final String newStatusValue =
command.stringValueOfParameterNamed(STATUS_PARAM);
+ final LoanOriginatorStatus newStatus =
LoanOriginatorStatus.fromString(newStatusValue);
+ originator.setStatus(newStatus);
+ changes.put(STATUS_PARAM, newStatusValue);
+ }
+
+ final Long currentOriginatorTypeId = originator.getOriginatorType() !=
null ? originator.getOriginatorType().getId() : null;
+ if (command.isChangeInLongParameterNamed(ORIGINATOR_TYPE_ID_PARAM,
currentOriginatorTypeId)) {
+ final CodeValue newOriginatorType = resolveCodeValue(command,
ORIGINATOR_TYPE_ID_PARAM, ORIGINATOR_TYPE_CODE_NAME);
+ originator.setOriginatorType(newOriginatorType);
+ changes.put(ORIGINATOR_TYPE_ID_PARAM, newOriginatorType != null ?
newOriginatorType.getId() : null);
+ }
+
+ final Long currentChannelTypeId = originator.getChannelType() != null
? originator.getChannelType().getId() : null;
+ if (command.isChangeInLongParameterNamed(CHANNEL_TYPE_ID_PARAM,
currentChannelTypeId)) {
+ final CodeValue newChannelType = resolveCodeValue(command,
CHANNEL_TYPE_ID_PARAM, CHANNEL_TYPE_CODE_NAME);
+ originator.setChannelType(newChannelType);
+ changes.put(CHANNEL_TYPE_ID_PARAM, newChannelType != null ?
newChannelType.getId() : null);
+ }
+
+ if (!changes.isEmpty()) {
+ this.loanOriginatorRepository.saveAndFlush(originator);
+ }
+
+ return new
CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(originator.getId())
+
.withEntityExternalId(originator.getExternalId()).with(changes).build();
+ }
+
+ @Override
+ public CommandProcessingResult delete(final Long id) {
+ final LoanOriginator originator =
this.loanOriginatorRepository.findById(id)
+ .orElseThrow(() -> new LoanOriginatorNotFoundException(id));
+
+ if (this.loanOriginatorMappingRepository.existsByOriginatorId(id)) {
+ throw new LoanOriginatorCannotBeDeletedException(id);
+ }
+
+ final ExternalId externalId = originator.getExternalId();
+ this.loanOriginatorRepository.delete(originator);
+
+ return new
CommandProcessingResultBuilder().withEntityId(id).withEntityExternalId(externalId).build();
+ }
+
+ private CodeValue resolveCodeValue(final JsonCommand command, final String
paramName, final String codeName) {
+ final Long codeValueId = command.longValueOfParameterNamed(paramName);
+ if (codeValueId == null) {
+ return null;
+ }
+ return
this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(codeName,
codeValueId);
+ }
+}
diff --git
a/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/module-changelog-master.xml
b/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/module-changelog-master.xml
index 6bc107ecab..9c8f305f09 100644
---
a/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/module-changelog-master.xml
+++
b/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/module-changelog-master.xml
@@ -23,4 +23,5 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<include relativeToChangelogFile="true"
file="parts/0001_initial_schema.xml"/>
+ <include relativeToChangelogFile="true" file="parts/0002_permissions.xml"/>
</databaseChangeLog>
diff --git
a/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/parts/0002_permissions.xml
b/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/parts/0002_permissions.xml
new file mode 100644
index 0000000000..1d95b6c437
--- /dev/null
+++
b/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/parts/0002_permissions.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+
+ <changeSet id="loan-origination-perm-001" author="fineract">
+ <preConditions onFail="MARK_RAN">
+ <sqlCheck expectedResult="0">SELECT COUNT(1) FROM m_permission
WHERE code = 'CREATE_LOAN_ORIGINATOR'</sqlCheck>
+ </preConditions>
+ <insert tableName="m_permission">
+ <column name="grouping" value="portfolio"/>
+ <column name="code" value="CREATE_LOAN_ORIGINATOR"/>
+ <column name="entity_name" value="LOAN_ORIGINATOR"/>
+ <column name="action_name" value="CREATE"/>
+ <column name="can_maker_checker" valueBoolean="false"/>
+ </insert>
+ </changeSet>
+
+ <changeSet id="loan-origination-perm-002" author="fineract">
+ <preConditions onFail="MARK_RAN">
+ <sqlCheck expectedResult="0">SELECT COUNT(1) FROM m_permission
WHERE code = 'READ_LOAN_ORIGINATOR'</sqlCheck>
+ </preConditions>
+ <insert tableName="m_permission">
+ <column name="grouping" value="portfolio"/>
+ <column name="code" value="READ_LOAN_ORIGINATOR"/>
+ <column name="entity_name" value="LOAN_ORIGINATOR"/>
+ <column name="action_name" value="READ"/>
+ <column name="can_maker_checker" valueBoolean="false"/>
+ </insert>
+ </changeSet>
+
+ <changeSet id="loan-origination-perm-003" author="fineract">
+ <preConditions onFail="MARK_RAN">
+ <sqlCheck expectedResult="0">SELECT COUNT(1) FROM m_permission
WHERE code = 'UPDATE_LOAN_ORIGINATOR'</sqlCheck>
+ </preConditions>
+ <insert tableName="m_permission">
+ <column name="grouping" value="portfolio"/>
+ <column name="code" value="UPDATE_LOAN_ORIGINATOR"/>
+ <column name="entity_name" value="LOAN_ORIGINATOR"/>
+ <column name="action_name" value="UPDATE"/>
+ <column name="can_maker_checker" valueBoolean="false"/>
+ </insert>
+ </changeSet>
+
+ <changeSet id="loan-origination-perm-004" author="fineract">
+ <preConditions onFail="MARK_RAN">
+ <sqlCheck expectedResult="0">SELECT COUNT(1) FROM m_permission
WHERE code = 'DELETE_LOAN_ORIGINATOR'</sqlCheck>
+ </preConditions>
+ <insert tableName="m_permission">
+ <column name="grouping" value="portfolio"/>
+ <column name="code" value="DELETE_LOAN_ORIGINATOR"/>
+ <column name="entity_name" value="LOAN_ORIGINATOR"/>
+ <column name="action_name" value="DELETE"/>
+ <column name="can_maker_checker" valueBoolean="false"/>
+ </insert>
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/application.properties
b/fineract-provider/src/main/resources/application.properties
index 93130efaeb..77c4ef0daf 100644
--- a/fineract-provider/src/main/resources/application.properties
+++ b/fineract-provider/src/main/resources/application.properties
@@ -212,7 +212,7 @@
fineract.sampling.resetPeriodSec=${FINERACT_SAMPLING_RESET_PERIOD_IN_SEC:60}
#Modules
fineract.module.self-service.enabled=${FINERACT_MODULE_SELF_SERVICE_ENABLED:false}
fineract.module.investor.enabled=${FINERACT_MODULE_INVESTOR_ENABLED:true}
-fineract.module.loan-origination.enabled=${FINERACT_MODULE_LOAN_ORIGINATION_ENABLED:false}
+fineract.module.loan-origination.enabled=${FINERACT_MODULE_LOAN_ORIGINATION_ENABLED:true}
fineract.insecure-http-client=${FINERACT_INSECURE_HTTP_CLIENT:true}
fineract.client-connect-timeout=${FINERACT_CLIENT_CONNECT_TIMEOUT:30}
diff --git a/fineract-provider/src/test/resources/application-test.properties
b/fineract-provider/src/test/resources/application-test.properties
index e5142d8468..d708c5b610 100644
--- a/fineract-provider/src/test/resources/application-test.properties
+++ b/fineract-provider/src/test/resources/application-test.properties
@@ -125,8 +125,7 @@ fineract.sampling.sampledClasses=
fineract.module.investor.enabled=true
fineract.module.self-service.enabled=true
-# TODO: activate this module when the implementation is ready
-fineract.module.loan-origination.enabled=false
+fineract.module.loan-origination.enabled=true
# sql validation
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanOriginatorApiTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanOriginatorApiTest.java
new file mode 100644
index 0000000000..576936b9ab
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanOriginatorApiTest.java
@@ -0,0 +1,280 @@
+/**
+ * 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 static org.junit.jupiter.api.Assertions.assertTrue;
+
+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.List;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.loans.LoanOriginatorHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+@Order(1)
+public class LoanOriginatorApiTest {
+
+ private RequestSpecification requestSpec;
+ private ResponseSpecification responseSpec;
+ private ResponseSpecification responseSpec400;
+ private ResponseSpecification responseSpec403;
+ private ResponseSpecification responseSpec404;
+
+ @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.responseSpec400 = new
ResponseSpecBuilder().expectStatusCode(400).build();
+ this.responseSpec403 = new
ResponseSpecBuilder().expectStatusCode(403).build();
+ this.responseSpec404 = new
ResponseSpecBuilder().expectStatusCode(404).build();
+ }
+
+ // ==================== CRUD LIFECYCLE TESTS ====================
+
+ @Test
+ public void testCreateOriginatorWithMinimalData() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+
+ // Create with only externalId
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId);
+
+ assertNotNull(originatorId, "Originator ID should not be null");
+
+ // Verify created data
+ final HashMap<String, Object> originator =
LoanOriginatorHelper.getOriginatorById(requestSpec, responseSpec, originatorId);
+
+ assertEquals(externalId, originator.get("externalId"));
+ assertEquals("ACTIVE", originator.get("status"), "Default status
should be ACTIVE");
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId);
+ }
+
+ @Test
+ public void testCreateOriginatorWithAllFields() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ final String name = "Test Originator";
+ final String status = "PENDING";
+
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId,
name, status, null, null);
+
+ assertNotNull(originatorId);
+
+ final HashMap<String, Object> originator =
LoanOriginatorHelper.getOriginatorById(requestSpec, responseSpec, originatorId);
+
+ assertEquals(externalId, originator.get("externalId"));
+ assertEquals(name, originator.get("name"));
+ assertEquals(status, originator.get("status"));
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId);
+ }
+
+ @Test
+ public void testRetrieveOriginatorById() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId);
+
+ final HashMap<String, Object> originator =
LoanOriginatorHelper.getOriginatorById(requestSpec, responseSpec, originatorId);
+
+ assertNotNull(originator);
+ assertEquals(originatorId, originator.get("id"));
+ assertEquals(externalId, originator.get("externalId"));
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId);
+ }
+
+ @Test
+ public void testRetrieveOriginatorByExternalId() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId);
+
+ final HashMap<String, Object> originator =
LoanOriginatorHelper.getOriginatorByExternalId(requestSpec, responseSpec,
externalId);
+
+ assertNotNull(originator);
+ assertEquals(originatorId, originator.get("id"));
+ assertEquals(externalId, originator.get("externalId"));
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId);
+ }
+
+ @Test
+ public void testRetrieveAllOriginators() {
+ final String externalId1 =
LoanOriginatorHelper.generateUniqueExternalId();
+ final String externalId2 =
LoanOriginatorHelper.generateUniqueExternalId();
+
+ final Integer originatorId1 =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId1);
+ final Integer originatorId2 =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId2);
+
+ final List<HashMap<String, Object>> originators =
LoanOriginatorHelper.getAllOriginators(requestSpec, responseSpec);
+
+ assertNotNull(originators);
+ assertTrue(originators.size() >= 2, "Should have at least 2
originators");
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId1);
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId2);
+ }
+
+ @Test
+ public void testUpdateOriginatorPartially() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId,
"Original Name", "ACTIVE",
+ null, null);
+
+ // Update only the name
+ final HashMap<String, Object> updateResult =
LoanOriginatorHelper.updateOriginator(requestSpec, responseSpec, originatorId,
+ "Updated Name", null, null, null);
+
+ assertNotNull(updateResult);
+ assertTrue(updateResult.containsKey("changes"), "Response should
contain changes");
+
+ // Verify update
+ final HashMap<String, Object> originator =
LoanOriginatorHelper.getOriginatorById(requestSpec, responseSpec, originatorId);
+ assertEquals("Updated Name", originator.get("name"));
+ assertEquals("ACTIVE", originator.get("status"), "Status should remain
unchanged");
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId);
+ }
+
+ @Test
+ public void testUpdateOriginatorByExternalId() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ LoanOriginatorHelper.createOriginator(requestSpec, responseSpec,
externalId, "Original Name", "ACTIVE", null, null);
+
+ // Update by external ID
+ final HashMap<String, Object> updateResult =
LoanOriginatorHelper.updateOriginatorByExternalId(requestSpec, responseSpec,
+ externalId, "Updated via ExternalId", null, null, null);
+
+ assertNotNull(updateResult);
+
+ // Verify update
+ final HashMap<String, Object> originator =
LoanOriginatorHelper.getOriginatorByExternalId(requestSpec, responseSpec,
externalId);
+ assertEquals("Updated via ExternalId", originator.get("name"));
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginatorByExternalId(requestSpec,
responseSpec, externalId);
+ }
+
+ @Test
+ public void testDeleteOriginator() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId);
+
+ // Delete
+ final Integer deletedId =
LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec, originatorId);
+ assertEquals(originatorId, deletedId);
+
+ // Verify deleted - should return 404
+ LoanOriginatorHelper.getOriginatorById(requestSpec, responseSpec404,
originatorId);
+ }
+
+ @Test
+ public void testDeleteOriginatorByExternalId() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId);
+
+ // Delete by external ID
+ final Integer deletedId =
LoanOriginatorHelper.deleteOriginatorByExternalId(requestSpec, responseSpec,
externalId);
+ assertEquals(originatorId, deletedId);
+
+ // Verify deleted - should return 404
+ LoanOriginatorHelper.getOriginatorByExternalId(requestSpec,
responseSpec404, externalId);
+ }
+
+ // ==================== VALIDATION ERROR TESTS ====================
+
+ @Test
+ public void testCreateOriginatorWithMissingExternalId() {
+ final String invalidJson = "{ \"name\": \"Test\" }";
+
+ LoanOriginatorHelper.createOriginatorExpectingError(requestSpec,
responseSpec400, invalidJson);
+ }
+
+ @Test
+ public void testCreateOriginatorWithDuplicateExternalId() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+
+ // Create first originator
+ final Integer originatorId =
LoanOriginatorHelper.createOriginator(requestSpec, responseSpec, externalId);
+
+ // Attempt to create duplicate - should return 403
+ final String duplicateJson = "{ \"externalId\": \"" + externalId + "\"
}";
+ LoanOriginatorHelper.createOriginatorExpectingError(requestSpec,
responseSpec403, duplicateJson);
+
+ // Cleanup
+ LoanOriginatorHelper.deleteOriginator(requestSpec, responseSpec,
originatorId);
+ }
+
+ @Test
+ public void testCreateOriginatorWithInvalidStatus() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ final String invalidJson = "{ \"externalId\": \"" + externalId + "\",
\"status\": \"INVALID\" }";
+
+ LoanOriginatorHelper.createOriginatorExpectingError(requestSpec,
responseSpec403, invalidJson);
+ }
+
+ @Test
+ public void testGetOriginatorByNonExistentId() {
+ LoanOriginatorHelper.getOriginatorById(requestSpec, responseSpec404,
999999);
+ }
+
+ @Test
+ public void testGetOriginatorByNonExistentExternalId() {
+ LoanOriginatorHelper.getOriginatorByExternalId(requestSpec,
responseSpec404, "NON-EXISTENT-EXTERNAL-ID");
+ }
+
+ @Test
+ public void testUpdateNonExistentOriginator() {
+ final String json = "{ \"name\": \"Updated\" }";
+ Utils.performServerPut(requestSpec, responseSpec404,
"/fineract-provider/api/v1/loan-originators/999999?" + Utils.TENANT_IDENTIFIER,
+ json, "");
+ }
+
+ @Test
+ public void testDeleteNonExistentOriginator() {
+ Utils.performServerDelete(requestSpec, responseSpec404,
+ "/fineract-provider/api/v1/loan-originators/999999?" +
Utils.TENANT_IDENTIFIER, "");
+ }
+
+ // ==================== CODE VALUE VALIDATION TESTS ====================
+
+ @Test
+ public void testCreateOriginatorWithInvalidCodeValueId() {
+ final String externalId =
LoanOriginatorHelper.generateUniqueExternalId();
+ // Use an ID that doesn't exist
+ final String json = "{ \"externalId\": \"" + externalId + "\",
\"originatorTypeId\": 999999 }";
+
+ LoanOriginatorHelper.createOriginatorExpectingError(requestSpec,
responseSpec404, json);
+ }
+}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanOriginatorHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanOriginatorHelper.java
new file mode 100644
index 0000000000..020f3e7a45
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanOriginatorHelper.java
@@ -0,0 +1,108 @@
+/**
+ * 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.client.feign.helpers;
+
+import static org.apache.fineract.client.feign.util.FeignCalls.fail;
+import static org.apache.fineract.client.feign.util.FeignCalls.ok;
+
+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.GetLoanOriginatorsResponse;
+import org.apache.fineract.client.models.PostLoanOriginatorsRequest;
+import org.apache.fineract.client.models.PostLoanOriginatorsResponse;
+import org.apache.fineract.client.models.PutLoanOriginatorsRequest;
+import org.apache.fineract.client.models.PutLoanOriginatorsResponse;
+
+public class FeignLoanOriginatorHelper {
+
+ private final FineractFeignClient fineractClient;
+
+ public FeignLoanOriginatorHelper(FineractFeignClient fineractClient) {
+ this.fineractClient = fineractClient;
+ }
+
+ public Long createOriginator(String externalId) {
+ return createOriginator(new
PostLoanOriginatorsRequest().externalId(externalId).name(externalId));
+ }
+
+ public Long createOriginator(String externalId, String name, String
status) {
+ return createOriginator(new
PostLoanOriginatorsRequest().externalId(externalId).name(name).status(status));
+ }
+
+ public Long createOriginator(PostLoanOriginatorsRequest request) {
+ PostLoanOriginatorsResponse response = ok(() ->
fineractClient.loanOriginators().create11(request));
+ return response.getResourceId();
+ }
+
+ public CallFailedRuntimeException
createOriginatorExpectingError(PostLoanOriginatorsRequest request) {
+ return fail(() -> fineractClient.loanOriginators().create11(request));
+ }
+
+ public List<GetLoanOriginatorsResponse> getAllOriginators() {
+ return ok(() -> fineractClient.loanOriginators().retrieveAll28());
+ }
+
+ public GetLoanOriginatorsResponse getOriginatorById(Long originatorId) {
+ return ok(() ->
fineractClient.loanOriginators().retrieveOne18(originatorId));
+ }
+
+ public CallFailedRuntimeException getOriginatorByIdExpectingError(Long
originatorId) {
+ return fail(() ->
fineractClient.loanOriginators().retrieveOne18(originatorId));
+ }
+
+ public GetLoanOriginatorsResponse getOriginatorByExternalId(String
externalId) {
+ return ok(() ->
fineractClient.loanOriginators().retrieveByExternalId(externalId));
+ }
+
+ public CallFailedRuntimeException
getOriginatorByExternalIdExpectingError(String externalId) {
+ return fail(() ->
fineractClient.loanOriginators().retrieveByExternalId(externalId));
+ }
+
+ public PutLoanOriginatorsResponse updateOriginator(Long originatorId,
PutLoanOriginatorsRequest request) {
+ return ok(() ->
fineractClient.loanOriginators().update16(originatorId, request));
+ }
+
+ public PutLoanOriginatorsResponse updateOriginatorByExternalId(String
externalId, PutLoanOriginatorsRequest request) {
+ return ok(() ->
fineractClient.loanOriginators().updateByExternalId(externalId, request));
+ }
+
+ public CallFailedRuntimeException updateOriginatorExpectingError(Long
originatorId, PutLoanOriginatorsRequest request) {
+ return fail(() ->
fineractClient.loanOriginators().update16(originatorId, request));
+ }
+
+ public Long deleteOriginator(Long originatorId) {
+ var response = ok(() ->
fineractClient.loanOriginators().delete14(originatorId));
+ return response.getResourceId();
+ }
+
+ public Long deleteOriginatorByExternalId(String externalId) {
+ var response = ok(() ->
fineractClient.loanOriginators().deleteByExternalId(externalId));
+ return response.getResourceId();
+ }
+
+ public CallFailedRuntimeException deleteOriginatorExpectingError(Long
originatorId) {
+ return fail(() ->
fineractClient.loanOriginators().delete14(originatorId));
+ }
+
+ public static String generateUniqueExternalId() {
+ return "EXT-" + UUID.randomUUID().toString().substring(0, 8);
+ }
+}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignLoanOriginatorApiTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignLoanOriginatorApiTest.java
new file mode 100644
index 0000000000..e150137277
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignLoanOriginatorApiTest.java
@@ -0,0 +1,248 @@
+/**
+ * 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.client.feign.tests;
+
+import java.util.List;
+import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
+import org.apache.fineract.client.models.GetLoanOriginatorsResponse;
+import org.apache.fineract.client.models.PostLoanOriginatorsRequest;
+import org.apache.fineract.client.models.PutLoanOriginatorsRequest;
+import org.apache.fineract.client.models.PutLoanOriginatorsResponse;
+import org.apache.fineract.integrationtests.client.FeignIntegrationTest;
+import
org.apache.fineract.integrationtests.client.feign.helpers.FeignLoanOriginatorHelper;
+import org.apache.fineract.integrationtests.common.FineractFeignClientHelper;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+@Order(1)
+public class FeignLoanOriginatorApiTest extends FeignIntegrationTest {
+
+ private static FeignLoanOriginatorHelper originatorHelper;
+
+ @BeforeAll
+ public static void setup() {
+ originatorHelper = new
FeignLoanOriginatorHelper(FineractFeignClientHelper.getFineractFeignClient());
+ }
+
+ @Test
+ public void testCreateOriginatorWithMinimalData() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+
+ final Long originatorId =
originatorHelper.createOriginator(externalId);
+
+ assertThat(originatorId).isNotNull();
+
+ final GetLoanOriginatorsResponse originator =
originatorHelper.getOriginatorById(originatorId);
+
+ assertThat(originator.getExternalId()).isEqualTo(externalId);
+ assertThat(originator.getStatus()).isEqualTo("ACTIVE");
+
+ originatorHelper.deleteOriginator(originatorId);
+ }
+
+ @Test
+ public void testCreateOriginatorWithAllFields() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final String name = "Test Originator";
+ final String status = "PENDING";
+
+ final Long originatorId =
originatorHelper.createOriginator(externalId, name, status);
+
+ assertThat(originatorId).isNotNull();
+
+ final GetLoanOriginatorsResponse originator =
originatorHelper.getOriginatorById(originatorId);
+
+ assertThat(originator.getExternalId()).isEqualTo(externalId);
+ assertThat(originator.getName()).isEqualTo(name);
+ assertThat(originator.getStatus()).isEqualTo(status);
+
+ originatorHelper.deleteOriginator(originatorId);
+ }
+
+ @Test
+ public void testRetrieveOriginatorById() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final Long originatorId =
originatorHelper.createOriginator(externalId);
+
+ final GetLoanOriginatorsResponse originator =
originatorHelper.getOriginatorById(originatorId);
+
+ assertThat(originator).isNotNull();
+ assertThat(originator.getId()).isEqualTo(originatorId);
+ assertThat(originator.getExternalId()).isEqualTo(externalId);
+
+ originatorHelper.deleteOriginator(originatorId);
+ }
+
+ @Test
+ public void testRetrieveOriginatorByExternalId() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final Long originatorId =
originatorHelper.createOriginator(externalId);
+
+ final GetLoanOriginatorsResponse originator =
originatorHelper.getOriginatorByExternalId(externalId);
+
+ assertThat(originator).isNotNull();
+ assertThat(originator.getId()).isEqualTo(originatorId);
+ assertThat(originator.getExternalId()).isEqualTo(externalId);
+
+ originatorHelper.deleteOriginator(originatorId);
+ }
+
+ @Test
+ public void testRetrieveAllOriginators() {
+ final String externalId1 =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final String externalId2 =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+
+ final Long originatorId1 =
originatorHelper.createOriginator(externalId1);
+ final Long originatorId2 =
originatorHelper.createOriginator(externalId2);
+
+ final List<GetLoanOriginatorsResponse> originators =
originatorHelper.getAllOriginators();
+
+ assertThat(originators).isNotNull();
+ assertThat(originators).hasSizeGreaterThanOrEqualTo(2);
+
+ originatorHelper.deleteOriginator(originatorId1);
+ originatorHelper.deleteOriginator(originatorId2);
+ }
+
+ @Test
+ public void testUpdateOriginatorPartially() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final Long originatorId =
originatorHelper.createOriginator(externalId, "Original Name", "ACTIVE");
+
+ final PutLoanOriginatorsResponse updateResult =
originatorHelper.updateOriginator(originatorId,
+ new PutLoanOriginatorsRequest().name("Updated Name"));
+
+ assertThat(updateResult).isNotNull();
+
+ final GetLoanOriginatorsResponse originator =
originatorHelper.getOriginatorById(originatorId);
+ assertThat(originator.getName()).isEqualTo("Updated Name");
+ assertThat(originator.getStatus()).isEqualTo("ACTIVE");
+
+ originatorHelper.deleteOriginator(originatorId);
+ }
+
+ @Test
+ public void testUpdateOriginatorByExternalId() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ originatorHelper.createOriginator(externalId, "Original Name",
"ACTIVE");
+
+ final PutLoanOriginatorsResponse updateResult =
originatorHelper.updateOriginatorByExternalId(externalId,
+ new PutLoanOriginatorsRequest().name("Updated via
ExternalId"));
+
+ assertThat(updateResult).isNotNull();
+
+ final GetLoanOriginatorsResponse originator =
originatorHelper.getOriginatorByExternalId(externalId);
+ assertThat(originator.getName()).isEqualTo("Updated via ExternalId");
+
+ originatorHelper.deleteOriginatorByExternalId(externalId);
+ }
+
+ @Test
+ public void testDeleteOriginator() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final Long originatorId =
originatorHelper.createOriginator(externalId);
+
+ final Long deletedId = originatorHelper.deleteOriginator(originatorId);
+ assertThat(deletedId).isEqualTo(originatorId);
+
+ final CallFailedRuntimeException exception =
originatorHelper.getOriginatorByIdExpectingError(originatorId);
+ assertThat(exception.getStatus()).isEqualTo(404);
+ }
+
+ @Test
+ public void testDeleteOriginatorByExternalId() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final Long originatorId =
originatorHelper.createOriginator(externalId);
+
+ final Long deletedId =
originatorHelper.deleteOriginatorByExternalId(externalId);
+ assertThat(deletedId).isEqualTo(originatorId);
+
+ final CallFailedRuntimeException exception =
originatorHelper.getOriginatorByExternalIdExpectingError(externalId);
+ assertThat(exception.getStatus()).isEqualTo(404);
+ }
+
+ @Test
+ public void testCreateOriginatorWithOptionalName() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final PostLoanOriginatorsRequest request = new
PostLoanOriginatorsRequest().externalId(externalId);
+
+ final Long originatorId = originatorHelper.createOriginator(request);
+ assertThat(originatorId).isNotNull();
+
+ originatorHelper.deleteOriginator(originatorId);
+ }
+
+ @Test
+ public void testCreateOriginatorWithDuplicateExternalId() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+
+ final Long originatorId =
originatorHelper.createOriginator(externalId);
+
+ final CallFailedRuntimeException exception = originatorHelper
+ .createOriginatorExpectingError(new
PostLoanOriginatorsRequest().externalId(externalId).name("Duplicate"));
+ assertThat(exception.getStatus()).isEqualTo(403);
+
+ originatorHelper.deleteOriginator(originatorId);
+ }
+
+ @Test
+ public void testCreateOriginatorWithInvalidStatus() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final PostLoanOriginatorsRequest request = new
PostLoanOriginatorsRequest().externalId(externalId).name("Test").status("INVALID");
+
+ final CallFailedRuntimeException exception =
originatorHelper.createOriginatorExpectingError(request);
+ assertThat(exception.getStatus()).isEqualTo(403);
+ }
+
+ @Test
+ public void testGetOriginatorByNonExistentId() {
+ final CallFailedRuntimeException exception =
originatorHelper.getOriginatorByIdExpectingError(999999L);
+ assertThat(exception.getStatus()).isEqualTo(404);
+ }
+
+ @Test
+ public void testGetOriginatorByNonExistentExternalId() {
+ final CallFailedRuntimeException exception =
originatorHelper.getOriginatorByExternalIdExpectingError("NON-EXISTENT-EXTERNAL-ID");
+ assertThat(exception.getStatus()).isEqualTo(404);
+ }
+
+ @Test
+ public void testUpdateNonExistentOriginator() {
+ final CallFailedRuntimeException exception =
originatorHelper.updateOriginatorExpectingError(999999L,
+ new PutLoanOriginatorsRequest().name("Updated"));
+ assertThat(exception.getStatus()).isEqualTo(404);
+ }
+
+ @Test
+ public void testDeleteNonExistentOriginator() {
+ final CallFailedRuntimeException exception =
originatorHelper.deleteOriginatorExpectingError(999999L);
+ assertThat(exception.getStatus()).isEqualTo(404);
+ }
+
+ @Test
+ public void testCreateOriginatorWithInvalidCodeValueId() {
+ final String externalId =
FeignLoanOriginatorHelper.generateUniqueExternalId();
+ final PostLoanOriginatorsRequest request = new
PostLoanOriginatorsRequest().externalId(externalId).name("Test")
+ .originatorTypeId(999999L);
+
+ final CallFailedRuntimeException exception =
originatorHelper.createOriginatorExpectingError(request);
+ assertThat(exception.getStatus()).isEqualTo(404);
+ }
+}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanOriginatorHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanOriginatorHelper.java
new file mode 100644
index 0000000000..716fc72258
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanOriginatorHelper.java
@@ -0,0 +1,166 @@
+/**
+ * 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.common.loans;
+
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import org.apache.fineract.integrationtests.common.Utils;
+
+public final class LoanOriginatorHelper {
+
+ private static final String LOAN_ORIGINATOR_URL =
"/fineract-provider/api/v1/loan-originators";
+
+ private LoanOriginatorHelper() {}
+
+ // ========== CREATE ==========
+
+ public static Integer createOriginator(final RequestSpecification
requestSpec, final ResponseSpecification responseSpec,
+ final String externalId) {
+ return createOriginator(requestSpec, responseSpec, externalId, null,
null, null, null);
+ }
+
+ public static Integer createOriginator(final RequestSpecification
requestSpec, final ResponseSpecification responseSpec,
+ final String externalId, final String name, final String status,
final Long originatorTypeId, final Long channelTypeId) {
+ final String json = buildCreateJson(externalId, name, status,
originatorTypeId, channelTypeId);
+ return Utils.performServerPost(requestSpec, responseSpec,
LOAN_ORIGINATOR_URL + "?" + Utils.TENANT_IDENTIFIER, json, "resourceId");
+ }
+
+ public static HashMap<String, Object>
createOriginatorWithFullResponse(final RequestSpecification requestSpec,
+ final ResponseSpecification responseSpec, final String externalId)
{
+ final String json = buildCreateJson(externalId, null, null, null,
null);
+ return Utils.performServerPost(requestSpec, responseSpec,
LOAN_ORIGINATOR_URL + "?" + Utils.TENANT_IDENTIFIER, json, "");
+ }
+
+ public static List<HashMap<String, Object>>
createOriginatorExpectingError(final RequestSpecification requestSpec,
+ final ResponseSpecification responseSpec, final String json) {
+ return Utils.performServerPost(requestSpec, responseSpec,
LOAN_ORIGINATOR_URL + "?" + Utils.TENANT_IDENTIFIER, json, "errors");
+ }
+
+ // ========== READ ==========
+
+ public static HashMap<String, Object> getOriginatorById(final
RequestSpecification requestSpec,
+ final ResponseSpecification responseSpec, final Integer
originatorId) {
+ return Utils.performServerGet(requestSpec, responseSpec,
LOAN_ORIGINATOR_URL + "/" + originatorId + "?" + Utils.TENANT_IDENTIFIER,
+ "");
+ }
+
+ public static HashMap<String, Object> getOriginatorByExternalId(final
RequestSpecification requestSpec,
+ final ResponseSpecification responseSpec, final String externalId)
{
+ return Utils.performServerGet(requestSpec, responseSpec,
+ LOAN_ORIGINATOR_URL + "/external-id/" + externalId + "?" +
Utils.TENANT_IDENTIFIER, "");
+ }
+
+ public static List<HashMap<String, Object>> getAllOriginators(final
RequestSpecification requestSpec,
+ final ResponseSpecification responseSpec) {
+ return Utils.performServerGet(requestSpec, responseSpec,
LOAN_ORIGINATOR_URL + "?" + Utils.TENANT_IDENTIFIER, "");
+ }
+
+ // ========== UPDATE ==========
+
+ public static HashMap<String, Object> updateOriginator(final
RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+ final Integer originatorId, final String name, final String
status, final Long originatorTypeId, final Long channelTypeId) {
+ final String json = buildUpdateJson(name, status, originatorTypeId,
channelTypeId);
+ return Utils.performServerPut(requestSpec, responseSpec,
LOAN_ORIGINATOR_URL + "/" + originatorId + "?" + Utils.TENANT_IDENTIFIER,
+ json, "");
+ }
+
+ public static HashMap<String, Object> updateOriginatorByExternalId(final
RequestSpecification requestSpec,
+ final ResponseSpecification responseSpec, final String externalId,
final String name, final String status,
+ final Long originatorTypeId, final Long channelTypeId) {
+ final String json = buildUpdateJson(name, status, originatorTypeId,
channelTypeId);
+ return Utils.performServerPut(requestSpec, responseSpec,
+ LOAN_ORIGINATOR_URL + "/external-id/" + externalId + "?" +
Utils.TENANT_IDENTIFIER, json, "");
+ }
+
+ // ========== DELETE ==========
+
+ public static Integer deleteOriginator(final RequestSpecification
requestSpec, final ResponseSpecification responseSpec,
+ final Integer originatorId) {
+ return Utils.performServerDelete(requestSpec, responseSpec,
+ LOAN_ORIGINATOR_URL + "/" + originatorId + "?" +
Utils.TENANT_IDENTIFIER, "resourceId");
+ }
+
+ public static Integer deleteOriginatorByExternalId(final
RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+ final String externalId) {
+ return Utils.performServerDelete(requestSpec, responseSpec,
+ LOAN_ORIGINATOR_URL + "/external-id/" + externalId + "?" +
Utils.TENANT_IDENTIFIER, "resourceId");
+ }
+
+ // ========== JSON BUILDERS ==========
+
+ private static String buildCreateJson(final String externalId, final
String name, final String status, final Long originatorTypeId,
+ final Long channelTypeId) {
+ final StringBuilder json = new StringBuilder("{");
+ json.append("\"externalId\": \"").append(externalId).append("\"");
+ if (name != null) {
+ json.append(", \"name\": \"").append(name).append("\"");
+ }
+ if (status != null) {
+ json.append(", \"status\": \"").append(status).append("\"");
+ }
+ if (originatorTypeId != null) {
+ json.append(", \"originatorTypeId\": ").append(originatorTypeId);
+ }
+ if (channelTypeId != null) {
+ json.append(", \"channelTypeId\": ").append(channelTypeId);
+ }
+ json.append("}");
+ return json.toString();
+ }
+
+ private static String buildUpdateJson(final String name, final String
status, final Long originatorTypeId, final Long channelTypeId) {
+ final StringBuilder json = new StringBuilder("{");
+ boolean first = true;
+ if (name != null) {
+ json.append("\"name\": \"").append(name).append("\"");
+ first = false;
+ }
+ if (status != null) {
+ if (!first) {
+ json.append(", ");
+ }
+ json.append("\"status\": \"").append(status).append("\"");
+ first = false;
+ }
+ if (originatorTypeId != null) {
+ if (!first) {
+ json.append(", ");
+ }
+ json.append("\"originatorTypeId\": ").append(originatorTypeId);
+ first = false;
+ }
+ if (channelTypeId != null) {
+ if (!first) {
+ json.append(", ");
+ }
+ json.append("\"channelTypeId\": ").append(channelTypeId);
+ }
+ json.append("}");
+ return json.toString();
+ }
+
+ // ========== UTILITIES ==========
+
+ public static String generateUniqueExternalId() {
+ return "EXT-" + UUID.randomUUID().toString().substring(0, 8);
+ }
+}