This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 045f0ec12 [FINERACT-1926] POST API for asset externalization
045f0ec12 is described below
commit 045f0ec121a0e5a5ed6a4335f50f6286ccc4a36b
Author: taskain7 <[email protected]>
AuthorDate: Tue May 23 07:00:58 2023 +0200
[FINERACT-1926] POST API for asset externalization
---
.../commands/service/CommandWrapperBuilder.java | 16 ++
...PortfolioCommandSourceWritePlatformService.java | 0
.../infrastructure/cache/domain/CacheType.java | 0
.../domain/ConfigurationDomainService.java | 0
.../core/data/CommandProcessingResultBuilder.java | 0
.../core/data/LoanIdAndExternalIdData.java | 14 +-
.../exception/UnrecognizedQueryParamException.java | 0
.../core/service/CommandParameterUtil.java | 0
.../core/service/ExternalIdFactory.java | 0
.../service/LoanReadPlatformServiceCommon.java | 11 +-
.../api/ExternalAssetOwnersApiResource.java | 46 +++-
.../api/ExternalAssetOwnersApiResourceSwagger.java | 69 ++++-
...rData.java => ExternalTransferChangedData.java} | 14 +-
.../investor/data/ExternalTransferData.java | 3 +-
...java => ExternalTransferRequestParameters.java} | 22 +-
.../ExternalTransferResponseData.java} | 16 +-
.../investor/domain/ExternalAssetOwner.java | 3 +-
.../domain/ExternalAssetOwnerRepository.java | 4 +
.../domain/ExternalAssetOwnerTransfer.java | 8 +-
.../ExternalAssetOwnerTransferRepository.java | 13 +-
.../investor}/domain/ExternalIdConverter.java | 6 +-
...ternalAssetOwnerInitiateTransferException.java} | 11 +-
...lAssetOwnerInitiateTransferExceptionMapper.java | 45 ++++
.../BuybackLoanFromExternalAssetOwnerHandler.java | 24 +-
.../ExternalAssetOwnersReadServiceImpl.java | 13 +-
.../service/ExternalAssetOwnersWriteService.java | 14 +-
.../ExternalAssetOwnersWriteServiceImpl.java | 215 +++++++++++++++
.../SaleLoanToExternalAssetOwnerHandler.java | 24 +-
.../module/investor/module-changelog-master.xml | 3 +
.../module/investor/parts/0003_asset_schemas.xml | 20 ++
.../0004_change_purchase_price_ratio_type.xml} | 17 +-
.../parts/0005_add_sale_and_buyback_command.xml | 41 +++
.../fineract/cob/data/LoanIdAndExternalId.java | 11 +-
.../core/auditing/CustomAuditingHandler.java | 7 +-
.../core/domain/ExternalIdConverter.java | 3 +-
.../loanaccount/domain/LoanRepository.java | 11 +
.../loanaccount/domain/LoanRepositoryWrapper.java | 4 +
.../service/LoanReadPlatformServiceImpl.java | 14 +-
.../ExternalAssetOwnerHelper.java | 53 ++++
.../InitiateExternalAssetOwnerTransferTest.java | 300 +++++++++++++++++++++
40 files changed, 967 insertions(+), 108 deletions(-)
diff --git
a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index 1ffc5265e..ca182beff 100644
---
a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++
b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -3622,4 +3622,20 @@ public class CommandWrapperBuilder {
this.href = "/loans/" + loanId +
"/transactions?command=undo-charge-off";
return this;
}
+
+ public CommandWrapperBuilder saleLoanToExternalAssetOwner(final Long
loanId) {
+ this.actionName = "SALE";
+ this.entityName = "LOAN";
+ this.loanId = loanId;
+ this.href = "/external-asset-owners/transfers/loans/" + loanId;
+ return this;
+ }
+
+ public CommandWrapperBuilder buybackLoanToExternalAssetOwner(final Long
loanId) {
+ this.actionName = "BUYBACK";
+ this.entityName = "LOAN";
+ this.loanId = loanId;
+ this.href = "/external-asset-owners/transfers/loans/" + loanId;
+ return this;
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
b/fineract-core/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
similarity index 100%
copy from
fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
copy to
fineract-core/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/domain/CacheType.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/cache/domain/CacheType.java
similarity index 100%
rename from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/cache/domain/CacheType.java
rename to
fineract-core/src/main/java/org/apache/fineract/infrastructure/cache/domain/CacheType.java
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
similarity index 100%
rename from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
rename to
fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResultBuilder.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResultBuilder.java
similarity index 100%
rename from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResultBuilder.java
rename to
fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResultBuilder.java
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/LoanIdAndExternalIdData.java
similarity index 71%
copy from
fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
copy to
fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/LoanIdAndExternalIdData.java
index eee4e5d8e..f09191a3a 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/LoanIdAndExternalIdData.java
@@ -16,12 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.investor.domain;
+package org.apache.fineract.infrastructure.core.data;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
-public interface ExternalAssetOwnerRepository
- extends JpaRepository<ExternalAssetOwner, Long>,
JpaSpecificationExecutor<ExternalAssetOwner> {
+@Data
+@AllArgsConstructor
+public class LoanIdAndExternalIdData {
+ private Long loanId;
+ private ExternalId loanExternalId;
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/UnrecognizedQueryParamException.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/exception/UnrecognizedQueryParamException.java
similarity index 100%
rename from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/UnrecognizedQueryParamException.java
rename to
fineract-core/src/main/java/org/apache/fineract/infrastructure/core/exception/UnrecognizedQueryParamException.java
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java
similarity index 100%
rename from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java
rename to
fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/CommandParameterUtil.java
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/ExternalIdFactory.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/ExternalIdFactory.java
similarity index 100%
rename from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/ExternalIdFactory.java
rename to
fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/ExternalIdFactory.java
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
b/fineract-core/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceCommon.java
similarity index 71%
copy from
fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
copy to
fineract-core/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceCommon.java
index eee4e5d8e..871be9ab2 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
+++
b/fineract-core/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceCommon.java
@@ -16,12 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.investor.domain;
+package org.apache.fineract.portfolio.loanaccount.service;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.apache.fineract.infrastructure.core.data.LoanIdAndExternalIdData;
-public interface ExternalAssetOwnerRepository
- extends JpaRepository<ExternalAssetOwner, Long>,
JpaSpecificationExecutor<ExternalAssetOwner> {
+public interface LoanReadPlatformServiceCommon {
+ LoanIdAndExternalIdData getTransferableLoanIdAndExternalId(Long loanId);
+
+ Long getLoanIdByLoanExternalId(String externalId);
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResource.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResource.java
index 6c9f55bac..c918e7dee 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResource.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResource.java
@@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -37,13 +38,20 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import lombok.RequiredArgsConstructor;
-import org.apache.commons.lang3.NotImplementedException;
+import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.commands.service.CommandWrapperBuilder;
+import
org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
import
org.apache.fineract.infrastructure.security.service.PlatformUserRightsContext;
import org.apache.fineract.investor.data.ExternalTransferData;
+import org.apache.fineract.investor.data.ExternalTransferResponseData;
import org.apache.fineract.investor.service.ExternalAssetOwnersReadService;
+import
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformServiceCommon;
import org.springframework.stereotype.Component;
@Path("/v1/external-asset-owners")
@@ -56,29 +64,40 @@ public class ExternalAssetOwnersApiResource {
private final ExternalAssetOwnersReadService
externalAssetOwnersReadService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final DefaultToApiJsonSerializer<ExternalTransferData>
apiJsonSerializerService;
+ private final DefaultToApiJsonSerializer<ExternalTransferResponseData>
postApiJsonSerializerService;
+ private final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService;
+ private final LoanReadPlatformServiceCommon loanReadPlatformService;
@POST
@Path("/transfers/loans/{loanId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferResponse.class))),
+ @ApiResponse(responseCode = "403", description = "Transfer cannot
be initiated") })
public String transferRequestWithLoanId(@PathParam("loanId") final Long
loanId,
@QueryParam("command") @Parameter(description = "command") final
String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
platformUserRightsContext.isAuthenticated();
-
- throw new NotImplementedException("Not implemented yet");
+ return getResult(loanId, apiRequestBodyAsJson, commandParam);
}
@POST
@Path("/transfers/loans/external-id/{loanExternalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String
transferRequestWithLoanExternalId(@PathParam("loanExternalId") final Long
loanId,
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferResponse.class))),
+ @ApiResponse(responseCode = "403", description = "Transfer cannot
be initiated") })
+ public String
transferRequestWithLoanExternalId(@PathParam("loanExternalId") final String
externalLoanId,
@QueryParam("command") @Parameter(description = "command") final
String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
platformUserRightsContext.isAuthenticated();
+ Long loanId =
loanReadPlatformService.getLoanIdByLoanExternalId(externalLoanId);
- throw new NotImplementedException("Not implemented yet");
+ return getResult(loanId, apiRequestBodyAsJson, commandParam);
}
@GET
@@ -99,4 +118,21 @@ public class ExternalAssetOwnersApiResource {
ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return apiJsonSerializerService.serialize(settings, transferDataList);
}
+
+ private String getResult(Long loanId, String apiRequestBodyAsJson, String
commandParam) {
+ final CommandWrapperBuilder builder = new
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "sale")) {
+ commandRequest =
builder.saleLoanToExternalAssetOwner(loanId).build();
+ } else if (CommandParameterUtil.is(commandParam, "buyback")) {
+ commandRequest =
builder.buybackLoanToExternalAssetOwner(loanId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam);
+ }
+ CommandProcessingResult result =
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return postApiJsonSerializerService.serialize(result);
+ }
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResourceSwagger.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResourceSwagger.java
index 43976241e..aba5bd722 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResourceSwagger.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/api/ExternalAssetOwnersApiResourceSwagger.java
@@ -19,7 +19,6 @@
package org.apache.fineract.investor.api;
import io.swagger.v3.oas.annotations.media.Schema;
-import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Set;
import org.apache.fineract.investor.data.ExternalTransferStatus;
@@ -68,7 +67,7 @@ final class ExternalAssetOwnersApiResourceSwagger {
public String transferExternalId;
@Schema(example = "1")
- public BigDecimal purchasePriceRatio;
+ public String purchasePriceRatio;
@Schema(example = "[2023, 5, 23]")
public LocalDate settlementDate;
@@ -87,4 +86,70 @@ final class ExternalAssetOwnersApiResourceSwagger {
public Integer totalFilteredRecords;
public Set<GetExternalTransferPageItems> pageItems;
}
+
+ @Schema(description = "PostInitiateTransferRequest")
+ public static final class PostInitiateTransferRequest {
+
+ private PostInitiateTransferRequest() {}
+
+ @Schema(example = "[2023, 5, 23]")
+ public LocalDate settlementDate;
+
+ @Schema(example = "1234567890987654321abc")
+ public String ownerExternalId;
+
+ @Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c1e")
+ public String transferExternalId;
+
+ @Schema(example = "1.2345678")
+ public String purchasePriceRatio;
+
+ @Schema(example = "yyyy-MM-dd")
+ public String dateformat;
+
+ @Schema(example = "en")
+ public String locale;
+ }
+
+ @Schema(description = "PostInitiateTransferResponse")
+ public static final class PostInitiateTransferResponse {
+
+ private PostInitiateTransferResponse() {}
+
+ @Schema(example = "1", description = "transfer ID")
+ public Long resourceId;
+
+ @Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c1e", description
= "transfer external ID")
+ public String resourceExternalId;
+
+ @Schema(example = "2", description = "loan ID")
+ public Long subResourceId;
+
+ @Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c2e", description
= "loan external ID")
+ public String subResourceExternalId;
+
+ public ExternalAssetOwnerTransferChangesData changes;
+
+ @Schema(example = "yyyy-MM-dd")
+ public String dateformat;
+
+ @Schema(example = "en")
+ public String locale;
+
+ @Schema(description = "ExternalAssetOwnerTransferChangesData")
+ static final class ExternalAssetOwnerTransferChangesData {
+
+ @Schema(example = "[2023, 5, 23]")
+ public String settlementDate;
+
+ @Schema(example = "1234567890987654321abc")
+ public String ownerExternalId;
+
+ @Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c1e")
+ public String transferExternalId;
+
+ @Schema(example = "1.23456789")
+ public String purchasePriceRatio;
+ }
+ }
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferChangedData.java
similarity index 73%
copy from
fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
copy to
fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferChangedData.java
index 23f2bb861..7df77203b 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferChangedData.java
@@ -18,20 +18,14 @@
*/
package org.apache.fineract.investor.data;
-import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Data;
@Data
-public class ExternalTransferData {
+public class ExternalTransferChangedData {
- private Long transferId;
- private ExternalTransferOwnerData owner;
- private ExternalTransferLoanMappingData loan;
- private String transferExternalId;
- private BigDecimal purchasePriceRatio;
private LocalDate settlementDate;
- private ExternalTransferStatus status;
- private LocalDate effectiveFrom;
- private LocalDate effectiveTo;
+ private String transferExternalId;
+ private String ownerExternalId;
+ private String purchasePriceRatio;
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
index 23f2bb861..ca1d1d878 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.investor.data;
-import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.Data;
@@ -29,7 +28,7 @@ public class ExternalTransferData {
private ExternalTransferOwnerData owner;
private ExternalTransferLoanMappingData loan;
private String transferExternalId;
- private BigDecimal purchasePriceRatio;
+ private String purchasePriceRatio;
private LocalDate settlementDate;
private ExternalTransferStatus status;
private LocalDate effectiveFrom;
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferRequestParameters.java
similarity index 62%
copy from
fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
copy to
fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferRequestParameters.java
index 23f2bb861..127b58727 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferRequestParameters.java
@@ -18,20 +18,14 @@
*/
package org.apache.fineract.investor.data;
-import java.math.BigDecimal;
-import java.time.LocalDate;
-import lombok.Data;
+public final class ExternalTransferRequestParameters {
-@Data
-public class ExternalTransferData {
+ private ExternalTransferRequestParameters() {}
- private Long transferId;
- private ExternalTransferOwnerData owner;
- private ExternalTransferLoanMappingData loan;
- private String transferExternalId;
- private BigDecimal purchasePriceRatio;
- private LocalDate settlementDate;
- private ExternalTransferStatus status;
- private LocalDate effectiveFrom;
- private LocalDate effectiveTo;
+ public static final String SETTLEMENT_DATE = "settlement_date";
+ public static final String OWNER_EXTERNAL_ID = "owner_external_id";
+ public static final String TRANSFER_EXTERNAL_ID = "transfer_external_id";
+ public static final String PURCHASE_PRICE_RATIO = "purchase_price_ratio";
+ public static final String DATEFORMAT = "dateformat";
+ public static final String LOCALE = "locale";
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferResponseData.java
similarity index 69%
copy from
fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
copy to
fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferResponseData.java
index eee4e5d8e..b6cc3bbd5 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferResponseData.java
@@ -16,12 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.investor.domain;
+package org.apache.fineract.investor.data;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import lombok.Data;
-public interface ExternalAssetOwnerRepository
- extends JpaRepository<ExternalAssetOwner, Long>,
JpaSpecificationExecutor<ExternalAssetOwner> {
+@Data
+public class ExternalTransferResponseData {
+ private Long resourceId;
+ private String resourceExternalId;
+ private Long subResourceId;
+ private String subResourceExternalId;
+ private ExternalTransferChangedData changes;
+ private String dateformat;
+ private String locale;
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwner.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwner.java
index fd7c4d3de..4f2422c78 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwner.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwner.java
@@ -21,7 +21,6 @@ package org.apache.fineract.investor.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
-import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@@ -30,7 +29,7 @@ import
org.apache.fineract.infrastructure.core.domain.ExternalId;
@Getter
@Setter
@Entity
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@NoArgsConstructor
@Table(name = "m_external_asset_owner")
public class ExternalAssetOwner extends AbstractAuditableWithUTCDateTimeCustom
{
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
index eee4e5d8e..90e554247 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
@@ -18,10 +18,14 @@
*/
package org.apache.fineract.investor.domain;
+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;
public interface ExternalAssetOwnerRepository
extends JpaRepository<ExternalAssetOwner, Long>,
JpaSpecificationExecutor<ExternalAssetOwner> {
+ Optional<ExternalAssetOwner> findByExternalId(ExternalId externalId);
+
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java
index e292ba6c0..468c3dc22 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java
@@ -18,14 +18,12 @@
*/
package org.apache.fineract.investor.domain;
-import java.math.BigDecimal;
import java.time.LocalDate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
-import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@@ -34,7 +32,7 @@ import
org.apache.fineract.infrastructure.core.domain.ExternalId;
@Getter
@Setter
@Table(name = "m_external_asset_owner_transfer")
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@NoArgsConstructor
@Entity
public class ExternalAssetOwnerTransfer extends
AbstractAuditableWithUTCDateTimeCustom {
@@ -51,8 +49,8 @@ public class ExternalAssetOwnerTransfer extends
AbstractAuditableWithUTCDateTime
@Column(name = "status", length = 50)
private String status;
- @Column(name = "purchase_price_ratio", precision = 19, scale = 6)
- private BigDecimal purchasePriceRatio;
+ @Column(name = "purchase_price_ratio", length = 50)
+ private String purchasePriceRatio;
@Column(name = "settlement_date")
private LocalDate settlementDate;
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferRepository.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferRepository.java
index 556c33fe6..799143d0a 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferRepository.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferRepository.java
@@ -19,6 +19,8 @@
package org.apache.fineract.investor.domain;
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.springframework.data.jpa.repository.Query;
@@ -27,7 +29,12 @@ import org.springframework.data.repository.query.Param;
public interface ExternalAssetOwnerTransferRepository
extends JpaRepository<ExternalAssetOwnerTransfer, Long>,
JpaSpecificationExecutor<ExternalAssetOwnerTransfer> {
- @Query("select e from ExternalAssetOwnerTransfer e where (:loanId is null
or e.loanId = :loanId) and (:loanExternalId is null or e.externalLoanId =
:loanExternalId) and (:transferExternalId is null or e.externalId =
:transferExternalId)")
- List<ExternalAssetOwnerTransfer> findAllByIncomingId(@Param("loanId") Long
loanId, @Param("loanExternalId") String loanExternalId,
- @Param("transferExternalId") String transferExternalId);
+ List<ExternalAssetOwnerTransfer> findAllByLoanId(Long loanId);
+
+ List<ExternalAssetOwnerTransfer> findAllByExternalLoanId(ExternalId
externalLoanId);
+
+ List<ExternalAssetOwnerTransfer> findAllByExternalId(ExternalId
externalId);
+
+ @Query("select e from ExternalAssetOwnerTransfer e where e.loanId =
:loanId and e.id = (select max(ex.id) from ExternalAssetOwnerTransfer ex where
ex.loanId = :loanId)")
+ Optional<ExternalAssetOwnerTransfer> findLatestByLoanId(@Param("loanId")
Long loanId);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/ExternalIdConverter.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalIdConverter.java
similarity index 85%
copy from
fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/ExternalIdConverter.java
copy to
fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalIdConverter.java
index e67504e77..b51da860a 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/ExternalIdConverter.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalIdConverter.java
@@ -16,11 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.infrastructure.core.domain;
+package org.apache.fineract.investor.domain;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
@Converter(autoApply = true)
public class ExternalIdConverter implements AttributeConverter<ExternalId,
String> {
@@ -32,7 +34,7 @@ public class ExternalIdConverter implements
AttributeConverter<ExternalId, Strin
@Override
public ExternalId convertToEntityAttribute(String externalId) {
- return StringUtils.isBlank(externalId) ? ExternalId.empty() : new
ExternalId(externalId);
+ return StringUtils.isBlank(externalId) ? ExternalId.empty() :
ExternalIdFactory.produce(externalId);
}
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/exception/ExternalAssetOwnerInitiateTransferException.java
similarity index 70%
copy from
fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
copy to
fineract-investor/src/main/java/org/apache/fineract/investor/exception/ExternalAssetOwnerInitiateTransferException.java
index eee4e5d8e..6fc4bcd8a 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/exception/ExternalAssetOwnerInitiateTransferException.java
@@ -16,12 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.investor.domain;
+package org.apache.fineract.investor.exception;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-
-public interface ExternalAssetOwnerRepository
- extends JpaRepository<ExternalAssetOwner, Long>,
JpaSpecificationExecutor<ExternalAssetOwner> {
+public class ExternalAssetOwnerInitiateTransferException extends
RuntimeException {
+ public ExternalAssetOwnerInitiateTransferException(String message) {
+ super(message);
+ }
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/exception/exceptionmapper/ExternalAssetOwnerInitiateTransferExceptionMapper.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/exception/exceptionmapper/ExternalAssetOwnerInitiateTransferExceptionMapper.java
new file mode 100644
index 000000000..bdae122d0
--- /dev/null
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/exception/exceptionmapper/ExternalAssetOwnerInitiateTransferExceptionMapper.java
@@ -0,0 +1,45 @@
+/**
+ * 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.investor.exception.exceptionmapper;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import
org.apache.fineract.investor.exception.ExternalAssetOwnerInitiateTransferException;
+import org.springframework.stereotype.Component;
+
+@Provider
+@Component
+@Slf4j
+public class ExternalAssetOwnerInitiateTransferExceptionMapper implements
ExceptionMapper<ExternalAssetOwnerInitiateTransferException> {
+
+ @Override
+ public Response toResponse(ExternalAssetOwnerInitiateTransferException
exception) {
+ final String globalisationMessageCode =
"error.msg.external.asset.owner.initiate";
+ final String defaultUserMessage = exception.getMessage();
+ log.warn("Exception: {}, Message: {}", exception.getClass().getName(),
defaultUserMessage);
+
+ final ApiParameterError error =
ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage);
+
+ return
Response.status(Response.Status.FORBIDDEN).entity(error).type(MediaType.APPLICATION_JSON).build();
+ }
+}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/BuybackLoanFromExternalAssetOwnerHandler.java
similarity index 54%
copy from
fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
copy to
fineract-investor/src/main/java/org/apache/fineract/investor/service/BuybackLoanFromExternalAssetOwnerHandler.java
index 7a897c547..9fc11ca38 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/BuybackLoanFromExternalAssetOwnerHandler.java
@@ -16,18 +16,24 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.commands.service;
+package org.apache.fineract.investor.service;
-import org.apache.fineract.commands.domain.CommandWrapper;
+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.springframework.stereotype.Service;
-public interface PortfolioCommandSourceWritePlatformService {
+@RequiredArgsConstructor
+@Service
+@CommandType(entity = "LOAN", action = "BUYBACK")
+public class BuybackLoanFromExternalAssetOwnerHandler implements
NewCommandSourceHandler {
- CommandProcessingResult logCommandSource(CommandWrapper commandRequest);
+ private final ExternalAssetOwnersWriteService
externalAssetOwnersWriteService;
- CommandProcessingResult approveEntry(Long id);
-
- Long rejectEntry(Long id);
-
- Long deleteEntry(Long makerCheckerId);
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return externalAssetOwnersWriteService.buyBackLoanByLoanId(command);
+ }
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersReadServiceImpl.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersReadServiceImpl.java
index b5b61db38..392fa9b24 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersReadServiceImpl.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersReadServiceImpl.java
@@ -18,8 +18,10 @@
*/
package org.apache.fineract.investor.service;
+import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.investor.data.ExternalTransferData;
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer;
import
org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
@@ -33,12 +35,17 @@ public class ExternalAssetOwnersReadServiceImpl implements
ExternalAssetOwnersRe
private final ExternalAssetOwnerTransferRepository
externalAssetOwnerTransferRepository;
private final ExternalAssetOwnersTransferMapper mapper;
- private final ExternalAssetOwnersTransferMapper
externalAssetOwnersTransferMapper;
@Override
public List<ExternalTransferData> retrieveTransferData(Long loanId, String
externalLoanId, String transferExternalId) {
- List<ExternalAssetOwnerTransfer> result =
externalAssetOwnerTransferRepository.findAllByIncomingId(loanId, externalLoanId,
- transferExternalId);
+ List<ExternalAssetOwnerTransfer> result = new ArrayList<>();
+ if (loanId != null) {
+
result.addAll(externalAssetOwnerTransferRepository.findAllByLoanId(loanId));
+ } else if (externalLoanId != null) {
+
result.addAll(externalAssetOwnerTransferRepository.findAllByExternalLoanId(ExternalIdFactory.produce(externalLoanId)));
+ } else if (transferExternalId != null) {
+
result.addAll(externalAssetOwnerTransferRepository.findAllByExternalId(ExternalIdFactory.produce(transferExternalId)));
+ }
return result.stream().map(mapper::mapTransfer).toList();
}
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteService.java
similarity index 70%
copy from
fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
copy to
fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteService.java
index 7a897c547..b3133875d 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteService.java
@@ -16,18 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.commands.service;
+package org.apache.fineract.investor.service;
-import org.apache.fineract.commands.domain.CommandWrapper;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
-public interface PortfolioCommandSourceWritePlatformService {
+public interface ExternalAssetOwnersWriteService {
- CommandProcessingResult logCommandSource(CommandWrapper commandRequest);
+ CommandProcessingResult saleLoanByLoanId(JsonCommand command);
- CommandProcessingResult approveEntry(Long id);
-
- Long rejectEntry(Long id);
-
- Long deleteEntry(Long makerCheckerId);
+ CommandProcessingResult buyBackLoanByLoanId(JsonCommand command);
}
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
new file mode 100644
index 000000000..50876c654
--- /dev/null
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
@@ -0,0 +1,215 @@
+/**
+ * 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.investor.service;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import lombok.RequiredArgsConstructor;
+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.data.LoanIdAndExternalIdData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.investor.data.ExternalTransferRequestParameters;
+import org.apache.fineract.investor.data.ExternalTransferStatus;
+import org.apache.fineract.investor.domain.ExternalAssetOwner;
+import org.apache.fineract.investor.domain.ExternalAssetOwnerRepository;
+import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer;
+import
org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
+import
org.apache.fineract.investor.exception.ExternalAssetOwnerInitiateTransferException;
+import
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformServiceCommon;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+public class ExternalAssetOwnersWriteServiceImpl implements
ExternalAssetOwnersWriteService {
+
+ private final ExternalAssetOwnerTransferRepository
externalAssetOwnerTransferRepository;
+ private final ExternalAssetOwnerRepository externalAssetOwnerRepository;
+ private final FromJsonHelper fromApiJsonHelper;
+ private final LoanReadPlatformServiceCommon loanReadPlatformService;
+
+ @Override
+ @Transactional
+ public CommandProcessingResult saleLoanByLoanId(JsonCommand command) {
+ Long loanId = command.getLoanId();
+ LoanIdAndExternalIdData loanIdAndExternalId =
loanReadPlatformService.getTransferableLoanIdAndExternalId(loanId);
+ ExternalAssetOwnerTransfer externalAssetOwnerTransfer =
parseJson(loanId, command.json(), loanIdAndExternalId.getLoanExternalId(),
+ ExternalTransferStatus.PENDING);
+ validateSale(externalAssetOwnerTransfer, loanIdAndExternalId);
+ ExternalAssetOwnerTransfer savedExternalAssetOwnerTransfer =
externalAssetOwnerTransferRepository.save(externalAssetOwnerTransfer);
+ return buildResponseData(savedExternalAssetOwnerTransfer);
+ }
+
+ @Override
+ @Transactional
+ public CommandProcessingResult buyBackLoanByLoanId(JsonCommand command) {
+ Long loanId = command.getLoanId();
+ LoanIdAndExternalIdData loanIdAndExternalId =
loanReadPlatformService.getTransferableLoanIdAndExternalId(loanId);
+ ExternalAssetOwnerTransfer externalAssetOwnerTransfer =
parseJson(command.getLoanId(), command.json(),
+ loanIdAndExternalId.getLoanExternalId(),
ExternalTransferStatus.BUYBACK);
+ validateBuyBack(externalAssetOwnerTransfer);
+ ExternalAssetOwnerTransfer savedExternalAssetOwnerTransfer =
externalAssetOwnerTransferRepository.save(externalAssetOwnerTransfer);
+ return buildResponseData(savedExternalAssetOwnerTransfer);
+ }
+
+ private CommandProcessingResult
buildResponseData(ExternalAssetOwnerTransfer savedExternalAssetOwnerTransfer) {
+ Map<String, Object> changes = new HashMap<>();
+ changes.put(ExternalTransferRequestParameters.SETTLEMENT_DATE,
savedExternalAssetOwnerTransfer.getSettlementDate());
+ changes.put(ExternalTransferRequestParameters.OWNER_EXTERNAL_ID,
+
savedExternalAssetOwnerTransfer.getOwner().getExternalId().getValue());
+ changes.put(ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID,
+
savedExternalAssetOwnerTransfer.getOwner().getExternalId().getValue());
+ changes.put(ExternalTransferRequestParameters.PURCHASE_PRICE_RATIO,
savedExternalAssetOwnerTransfer.getPurchasePriceRatio());
+ return new
CommandProcessingResultBuilder().withEntityId(savedExternalAssetOwnerTransfer.getId())
+
.withEntityExternalId(savedExternalAssetOwnerTransfer.getExternalId())
+ .withSubEntityId(savedExternalAssetOwnerTransfer.getLoanId())
+
.withSubEntityExternalId(Objects.isNull(savedExternalAssetOwnerTransfer.getExternalLoanId())
? null
+ : savedExternalAssetOwnerTransfer.getExternalLoanId())
+ .with(changes).build();
+ }
+
+ private void validateSale(ExternalAssetOwnerTransfer
externalAssetOwnerTransfer, LoanIdAndExternalIdData loanIdAndExternalId) {
+ validateSettlementDate(externalAssetOwnerTransfer);
+ validateTransferStatusForSale(externalAssetOwnerTransfer);
+ validateLoanStatus(loanIdAndExternalId);
+ }
+
+ private void validateBuyBack(ExternalAssetOwnerTransfer
externalAssetOwnerTransfer) {
+ validateSettlementDate(externalAssetOwnerTransfer);
+ validateTransferStatusForBuyBack(externalAssetOwnerTransfer);
+ }
+
+ private void validateSettlementDate(ExternalAssetOwnerTransfer
externalAssetOwnerTransfer) {
+ if
(externalAssetOwnerTransfer.getSettlementDate().isBefore(ThreadLocalContextUtil.getBusinessDate()))
{
+ throw new ExternalAssetOwnerInitiateTransferException("Settlement
date cannot be in the past");
+ }
+ }
+
+ private void validateLoanStatus(LoanIdAndExternalIdData
loanIdAndExternalIdAndExternalId) {
+ if (Objects.isNull(loanIdAndExternalIdAndExternalId)) {
+ throw new ExternalAssetOwnerInitiateTransferException("Loan is not
in active status");
+ }
+ }
+
+ private void validateTransferStatusForSale(ExternalAssetOwnerTransfer
externalAssetOwnerTransfer) {
+ Optional<ExternalAssetOwnerTransfer> latestTransferOptional =
externalAssetOwnerTransferRepository
+ .findLatestByLoanId(externalAssetOwnerTransfer.getLoanId());
+ if (latestTransferOptional.isPresent()) {
+ ExternalAssetOwnerTransfer latestTransfer =
latestTransferOptional.get();
+ ExternalTransferStatus latestTransferStatus =
ExternalTransferStatus.valueOf(latestTransfer.getStatus());
+ if (latestTransferStatus.equals(ExternalTransferStatus.PENDING)) {
+ throw new ExternalAssetOwnerInitiateTransferException(
+ "External asset owner transfer is already in PENDING
state for this loan.");
+ } else if
(latestTransferStatus.equals(ExternalTransferStatus.ACTIVE)) {
+ throw new ExternalAssetOwnerInitiateTransferException(
+ "This loan cannot be sold, because it is owned by an
external asset owner.");
+ }
+ }
+ }
+
+ private void validateTransferStatusForBuyBack(ExternalAssetOwnerTransfer
externalAssetOwnerTransfer) {
+ Optional<ExternalAssetOwnerTransfer> latestTransferOptional =
externalAssetOwnerTransferRepository
+ .findLatestByLoanId(externalAssetOwnerTransfer.getLoanId());
+ if (latestTransferOptional.isEmpty()) {
+ throw new ExternalAssetOwnerInitiateTransferException(
+ "This loan cannot be bought back, because it is not owned
by an external asset owner");
+ } else {
+ ExternalAssetOwnerTransfer latestTransfer =
latestTransferOptional.get();
+ ExternalTransferStatus latestTransferStatus =
ExternalTransferStatus.valueOf(latestTransfer.getStatus());
+ if (latestTransferStatus.equals(ExternalTransferStatus.BUYBACK)) {
+ throw new ExternalAssetOwnerInitiateTransferException(
+ "External asset owner transfer is already in BUYBACK
state for this loan.");
+ }
+ }
+ }
+
+ private ExternalAssetOwnerTransfer parseJson(Long loanId, String
apiRequestBodyAsJson, ExternalId externalLoanId,
+ ExternalTransferStatus status) {
+ ExternalAssetOwnerTransfer externalAssetOwnerTransfer = new
ExternalAssetOwnerTransfer();
+
+ validateRequestBody(apiRequestBodyAsJson);
+ final JsonElement json = fromApiJsonHelper.parse(apiRequestBodyAsJson);
+
+ ExternalAssetOwner owner = getOwner(json);
+ externalAssetOwnerTransfer.setOwnerId(owner.getId());
+ externalAssetOwnerTransfer.setOwner(owner);
+
externalAssetOwnerTransfer.setExternalId(getTransferExternalIdFromJson(json));
+ externalAssetOwnerTransfer.setStatus(status.name());
+
externalAssetOwnerTransfer.setPurchasePriceRatio(getPurchasePriceRatioFromJson(json));
+
externalAssetOwnerTransfer.setSettlementDate(getSettlementDateFromJson(json));
+
externalAssetOwnerTransfer.setEffectiveDateFrom(ThreadLocalContextUtil.getBusinessDate());
+ externalAssetOwnerTransfer.setEffectiveDateTo(LocalDate.of(9999, 12,
31));
+ externalAssetOwnerTransfer.setLoanId(loanId);
+ externalAssetOwnerTransfer.setExternalLoanId(externalLoanId);
+ return externalAssetOwnerTransfer;
+ }
+
+ private void validateRequestBody(String apiRequestBodyAsJson) {
+ final Set<String> requestParameters = new HashSet<>(
+
Arrays.asList(ExternalTransferRequestParameters.SETTLEMENT_DATE,
ExternalTransferRequestParameters.OWNER_EXTERNAL_ID,
+
ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID,
ExternalTransferRequestParameters.PURCHASE_PRICE_RATIO,
+ ExternalTransferRequestParameters.DATEFORMAT,
ExternalTransferRequestParameters.LOCALE));
+ final Type typeOfMap = new TypeToken<Map<String, Object>>()
{}.getType();
+ fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap,
apiRequestBodyAsJson, requestParameters);
+ }
+
+ private LocalDate getSettlementDateFromJson(JsonElement json) {
+ String dateFormat =
fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.DATEFORMAT,
json);
+ String locale =
fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.LOCALE,
json);
+ return
fromApiJsonHelper.extractLocalDateNamed(ExternalTransferRequestParameters.SETTLEMENT_DATE,
json, dateFormat,
+ JsonParserHelper.localeFromString(locale));
+ }
+
+ private ExternalId getTransferExternalIdFromJson(JsonElement json) {
+ String transferExternalId =
fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID,
json);
+ return ExternalIdFactory.produce(transferExternalId);
+ }
+
+ private String getPurchasePriceRatioFromJson(JsonElement json) {
+ return
fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.PURCHASE_PRICE_RATIO,
json);
+ }
+
+ private ExternalAssetOwner getOwner(JsonElement json) {
+ String ownerExternalId =
fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.OWNER_EXTERNAL_ID,
json);
+ Optional<ExternalAssetOwner> byExternalId =
externalAssetOwnerRepository
+ .findByExternalId(ExternalIdFactory.produce(ownerExternalId));
+ return byExternalId.orElseGet(() ->
createAndGetAssetOwner(ownerExternalId));
+ }
+
+ private ExternalAssetOwner createAndGetAssetOwner(String externalId) {
+ ExternalAssetOwner externalAssetOwner = new ExternalAssetOwner();
+
externalAssetOwner.setExternalId(ExternalIdFactory.produce(externalId));
+ return externalAssetOwnerRepository.save(externalAssetOwner);
+ }
+}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/SaleLoanToExternalAssetOwnerHandler.java
similarity index 54%
rename from
fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
rename to
fineract-investor/src/main/java/org/apache/fineract/investor/service/SaleLoanToExternalAssetOwnerHandler.java
index 7a897c547..39c56a1ef 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformService.java
+++
b/fineract-investor/src/main/java/org/apache/fineract/investor/service/SaleLoanToExternalAssetOwnerHandler.java
@@ -16,18 +16,24 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.commands.service;
+package org.apache.fineract.investor.service;
-import org.apache.fineract.commands.domain.CommandWrapper;
+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.springframework.stereotype.Service;
-public interface PortfolioCommandSourceWritePlatformService {
+@RequiredArgsConstructor
+@Service
+@CommandType(entity = "LOAN", action = "SALE")
+public class SaleLoanToExternalAssetOwnerHandler implements
NewCommandSourceHandler {
- CommandProcessingResult logCommandSource(CommandWrapper commandRequest);
+ private final ExternalAssetOwnersWriteService
externalAssetOwnersWriteService;
- CommandProcessingResult approveEntry(Long id);
-
- Long rejectEntry(Long id);
-
- Long deleteEntry(Long makerCheckerId);
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return externalAssetOwnersWriteService.saleLoanByLoanId(command);
+ }
}
diff --git
a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml
index 7f959be36..c7fa915cc 100644
---
a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml
+++
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml
@@ -24,4 +24,7 @@
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_asset_schemas.xml"/>
+ <include relativeToChangelogFile="true" file="parts/0003_asset_schemas.xml"/>
+ <include relativeToChangelogFile="true"
file="parts/0004_change_purchase_price_ratio_type.xml"/>
+ <include relativeToChangelogFile="true"
file="parts/0005_add_sale_and_buyback_command.xml"/>
</databaseChangeLog>
diff --git
a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0003_asset_schemas.xml
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0003_asset_schemas.xml
index bd12acd21..6730c2e06 100644
---
a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0003_asset_schemas.xml
+++
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0003_asset_schemas.xml
@@ -44,4 +44,24 @@
</changeSet>
+ <changeSet author="fineract" id="3" context="mysql">
+
+ <addColumn tableName="m_external_asset_owner_transfer">
+ <column name="external_loan_id" type="varchar(100)"/>
+ <column name="loan_id" type="bigint" remarks="Loan ID">
+ <constraints nullable="false"/>
+ </column>
+ </addColumn>
+ </changeSet>
+ <changeSet author="fineract" id="4" context="mysql">
+
+ <addForeignKeyConstraint baseColumnNames="loan_id"
+ baseTableName="m_external_asset_owner_transfer"
+
constraintName="FK_external_asset_owner_transfer_loan_id" deferrable="false"
+ initiallyDeferred="false"
+ onDelete="RESTRICT" onUpdate="RESTRICT"
referencedColumnNames="id"
+ referencedTableName="m_loan" validate="true"/>
+
+ </changeSet>
+
</databaseChangeLog>
diff --git
a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0004_change_purchase_price_ratio_type.xml
similarity index 56%
copy from
fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml
copy to
fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0004_change_purchase_price_ratio_type.xml
index 7f959be36..f5caa3fe6 100644
---
a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml
+++
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0004_change_purchase_price_ratio_type.xml
@@ -20,8 +20,17 @@
-->
<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">
- <include relativeToChangelogFile="true"
file="parts/0001_initial_schema.xml"/>
- <include relativeToChangelogFile="true" file="parts/0002_asset_schemas.xml"/>
+ 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 author="fineract" id="1" context="postgresql">
+ <modifyDataType columnName="purchase_price_ratio"
+ newDataType="VARCHAR(50)"
+ tableName="m_external_asset_owner_transfer"/>
+ </changeSet>
+ <changeSet author="fineract" id="2" context="mysql">
+ <modifyDataType columnName="purchase_price_ratio"
+ newDataType="VARCHAR(50)"
+ tableName="m_external_asset_owner_transfer"/>
+ </changeSet>
</databaseChangeLog>
diff --git
a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0005_add_sale_and_buyback_command.xml
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0005_add_sale_and_buyback_command.xml
new file mode 100644
index 000000000..802fabc66
--- /dev/null
+++
b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0005_add_sale_and_buyback_command.xml
@@ -0,0 +1,41 @@
+<?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="1" author="fineract">
+ <insert tableName="m_permission">
+ <column name="grouping" value="transaction_loan"/>
+ <column name="code" value="SALE_LOAN"/>
+ <column name="entity_name" value="LOAN"/>
+ <column name="action_name" value="SALE"/>
+ <column name="can_maker_checker" valueBoolean="false"/>
+ </insert>
+ <insert tableName="m_permission">
+ <column name="grouping" value="transaction_loan"/>
+ <column name="code" value="BUYBACK_LOAN"/>
+ <column name="entity_name" value="LOAN"/>
+ <column name="action_name" value="BUYBACK"/>
+ <column name="can_maker_checker" valueBoolean="false"/>
+ </insert>
+ </changeSet>
+</databaseChangeLog>
diff --git
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
b/fineract-provider/src/main/java/org/apache/fineract/cob/data/LoanIdAndExternalId.java
similarity index 71%
copy from
fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
copy to
fineract-provider/src/main/java/org/apache/fineract/cob/data/LoanIdAndExternalId.java
index eee4e5d8e..40a0584f2 100644
---
a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerRepository.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/cob/data/LoanIdAndExternalId.java
@@ -16,12 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.investor.domain;
+package org.apache.fineract.cob.data;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
-public interface ExternalAssetOwnerRepository
- extends JpaRepository<ExternalAssetOwner, Long>,
JpaSpecificationExecutor<ExternalAssetOwner> {
+public interface LoanIdAndExternalId {
+ Long getId();
+
+ ExternalId getExternalId();
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/auditing/CustomAuditingHandler.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/auditing/CustomAuditingHandler.java
index 74910ae8b..e1268e9a1 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/auditing/CustomAuditingHandler.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/auditing/CustomAuditingHandler.java
@@ -60,7 +60,12 @@ public class CustomAuditingHandler extends AuditingHandler {
}
private DateTimeProvider fetchDateTimeProvider(Object bean) {
- return bean instanceof AbstractAuditableWithUTCDateTimeCustom ?
CustomDateTimeProvider.TENANT : CustomDateTimeProvider.INSTANCE;
+ if (bean instanceof AbstractAuditableWithUTCDateTimeCustom
+ || bean instanceof
org.apache.fineract.investor.domain.AbstractAuditableWithUTCDateTimeCustom) {
+ return CustomDateTimeProvider.TENANT;
+ } else {
+ return CustomDateTimeProvider.INSTANCE;
+ }
}
/**
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/ExternalIdConverter.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/ExternalIdConverter.java
index e67504e77..f04af3057 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/ExternalIdConverter.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/ExternalIdConverter.java
@@ -21,6 +21,7 @@ package org.apache.fineract.infrastructure.core.domain;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
@Converter(autoApply = true)
public class ExternalIdConverter implements AttributeConverter<ExternalId,
String> {
@@ -32,7 +33,7 @@ public class ExternalIdConverter implements
AttributeConverter<ExternalId, Strin
@Override
public ExternalId convertToEntityAttribute(String externalId) {
- return StringUtils.isBlank(externalId) ? ExternalId.empty() : new
ExternalId(externalId);
+ return StringUtils.isBlank(externalId) ? ExternalId.empty() :
ExternalIdFactory.produce(externalId);
}
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
index 09f502a32..085cc59b7 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
@@ -85,6 +85,11 @@ public interface LoanRepository extends JpaRepository<Loan,
Long>, JpaSpecificat
String FIND_BY_ACCOUNT_NUMBER = "select loan from Loan loan where
loan.accountNumber = :accountNumber";
+ String GET_NON_CLOSED_LOAN_BY_LOAN_ID = "select loan from Loan loan where
loan.id = :loanId and loan.loanStatus in (100,200,300,"
+ + "303,304)";
+
+ String EXISTS_NON_CLOSED_BY_EXTERNAL_LOAN_ID = "select case when (count
(loan) > 0) then 'true' else 'false' end from Loan loan where loan.externalId =
:externalLoanId and loan.loanStatus in (100,200,300,303,304)";
+
String FIND_ID_BY_EXTERNAL_ID = "SELECT loan.id FROM Loan loan WHERE
loan.externalId = :externalId";
// should follow the logic of
`FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE` query
@@ -191,6 +196,12 @@ public interface LoanRepository extends
JpaRepository<Loan, Long>, JpaSpecificat
@Query(FIND_BY_ACCOUNT_NUMBER)
Loan findLoanAccountByAccountNumber(@Param("accountNumber") String
accountNumber);
+ @Query(GET_NON_CLOSED_LOAN_BY_LOAN_ID)
+ Loan getNonClosedLoanIdAndExternalIdByLoanId(@Param("loanId") Long loanId);
+
+ @Query(EXISTS_NON_CLOSED_BY_EXTERNAL_LOAN_ID)
+ boolean existsNonClosedLoanByExternalLoanId(@Param("externalLoanId")
ExternalId externalLoanId);
+
boolean existsByExternalId(@Param("externalId") ExternalId externalId);
@Query(FIND_ALL_NON_CLOSED)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java
index 497335802..f861c3869 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java
@@ -262,4 +262,8 @@ public class LoanRepositoryWrapper {
public List<Long> findLoanIdsByStatusId(Integer statusId) {
return repository.findLoanIdByStatusId(statusId);
}
+
+ public Loan getNonClosedLoanIdAndExternalIdByLoanId(Long loanId) {
+ return repository.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 8ea66f57c..60754d1b3 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -41,6 +41,7 @@ import
org.apache.fineract.infrastructure.codes.data.CodeValueData;
import
org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.data.LoanIdAndExternalIdData;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@@ -144,7 +145,7 @@ import org.springframework.util.CollectionUtils;
@AllArgsConstructor
@Service
@Transactional(readOnly = true)
-public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+public class LoanReadPlatformServiceImpl implements LoanReadPlatformService,
LoanReadPlatformServiceCommon {
private static final String ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE =
"submitted-date";
private final JdbcTemplate jdbcTemplate;
@@ -591,6 +592,17 @@ public class LoanReadPlatformServiceImpl implements
LoanReadPlatformService {
}
}
+ @Override
+ public LoanIdAndExternalIdData getTransferableLoanIdAndExternalId(Long
loanId) {
+ Loan loan =
loanRepositoryWrapper.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
+ return new LoanIdAndExternalIdData(loan.getId(), loan.getExternalId());
+ }
+
+ @Override
+ public Long getLoanIdByLoanExternalId(String externalId) {
+ return
loanRepositoryWrapper.findIdByExternalId(ExternalIdFactory.produce(externalId));
+ }
+
private static final class LoanMapper implements
RowMapper<LoanAccountData> {
private final DatabaseSpecificSQLGenerator sqlGenerator;
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/ExternalAssetOwnerHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/ExternalAssetOwnerHelper.java
new file mode 100644
index 000000000..26f9538af
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/ExternalAssetOwnerHelper.java
@@ -0,0 +1,53 @@
+/**
+ * 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.investor.externalassetowner;
+
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import org.apache.fineract.integrationtests.client.IntegrationTest;
+import org.apache.fineract.integrationtests.common.Utils;
+
+public class ExternalAssetOwnerHelper extends IntegrationTest {
+
+ private final RequestSpecification requestSpec;
+ private final ResponseSpecification responseSpec;
+
+ public ExternalAssetOwnerHelper(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec) {
+ this.requestSpec = requestSpec;
+ this.responseSpec = responseSpec;
+ }
+
+ public String initiateTransferByLoanId(Long loanId, String command, String
json) {
+ final String INITIATE_TRANSFER_URL =
"/fineract-provider/api/v1/external-asset-owners/transfers/loans/" + loanId +
"?"
+ + Utils.TENANT_IDENTIFIER + "&command=" + command;
+ return Utils.performServerPost(requestSpec, responseSpec,
INITIATE_TRANSFER_URL, json);
+ }
+
+ public String retrieveTransferByTransferExternalId(String
transferExternalId) {
+ final String RETRIEVE_TRANSFER_URL =
"/fineract-provider/api/v1/external-asset-owners/transfers?" +
Utils.TENANT_IDENTIFIER
+ + "&transferExternalId=" + transferExternalId;
+ return Utils.performServerGet(requestSpec, responseSpec,
RETRIEVE_TRANSFER_URL);
+ }
+
+ public String retrieveTransferByLoanId(Long loanId) {
+ final String RETRIEVE_TRANSFER_URL =
"/fineract-provider/api/v1/external-asset-owners/transfers?" +
Utils.TENANT_IDENTIFIER
+ + "&loanId=" + loanId;
+ return Utils.performServerGet(requestSpec, responseSpec,
RETRIEVE_TRANSFER_URL);
+ }
+}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
new file mode 100644
index 000000000..fe78adeab
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
@@ -0,0 +1,300 @@
+/**
+ * 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.investor.externalassetowner;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.path.json.JsonPath;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class InitiateExternalAssetOwnerTransferTest {
+
+ private ResponseSpecification responseSpec;
+ private ResponseSpecification responseSpecError;
+ private RequestSpecification requestSpec;
+ private ExternalAssetOwnerHelper externalAssetOwnerHelper;
+ private LoanTransactionHelper loanTransactionHelper;
+ private LocalDate todaysDate;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " +
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ responseSpecError = new
ResponseSpecBuilder().expectStatusCode(403).build();
+ externalAssetOwnerHelper = new ExternalAssetOwnerHelper(requestSpec,
responseSpec);
+ loanTransactionHelper = new LoanTransactionHelper(requestSpec,
responseSpec);
+
+ todaysDate = Utils.getLocalDateOfTenant();
+ }
+
+ @Test
+ public void saleActiveLoanToExternalAssetOwner() {
+ try {
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.TRUE);
+
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
+
GlobalConfigurationHelper.updateValueForGlobalConfiguration(requestSpec,
responseSpec, "10", "0");
+
+ final Integer clientID = ClientHelper.createClient(requestSpec,
responseSpec);
+ Assertions.assertNotNull(clientID);
+
+ Integer overdueFeeChargeId =
ChargesHelper.createCharges(requestSpec, responseSpec,
+
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
+ Assertions.assertNotNull(overdueFeeChargeId);
+
+ final Integer loanProductID =
createLoanProduct(overdueFeeChargeId.toString());
+ Assertions.assertNotNull(loanProductID);
+ HashMap loanStatusHashMap;
+
+ final Integer loanID =
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null,
"10 January 2020");
+
+ Assertions.assertNotNull(loanID);
+
+ loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec,
responseSpec, loanID);
+ LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+ loanStatusHashMap = loanTransactionHelper.approveLoan("01 March
2020", loanID);
+ LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+
+ String loanDetails =
loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
+ loanStatusHashMap =
loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020",
loanID,
+
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+ LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+ String transferExternalId = "36efeb06-d835-48a1-99eb-09bd1d348c1e";
+ String saleResponse =
externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "sale",
+ getSaleRequestJson("04 March 2020", transferExternalId));
+ Type type = new TypeToken<Map<String, Object>>() {}.getType();
+ Map<String, Object> responseMap = new
Gson().fromJson(saleResponse, type);
+ assertEquals(responseMap.get("resourceExternalId"),
transferExternalId);
+
+ String retrieveResponse =
externalAssetOwnerHelper.retrieveTransferByLoanId(loanID.longValue());
+ Type retrieveType = new TypeToken<List<Map<String, Object>>>()
{}.getType();
+ List<Map<String, Object>> retrieveResponseMap = new
Gson().fromJson(retrieveResponse, retrieveType);
+ assertEquals(1, retrieveResponseMap.size());
+ assertEquals(retrieveResponseMap.get(0).get("transferExternalId"),
transferExternalId);
+ assertEquals(retrieveResponseMap.get(0).get("status"), "PENDING");
+ } finally {
+ requestSpec = new
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " +
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ requestSpec.header("Fineract-Platform-TenantId", "default");
+ responseSpec = new
ResponseSpecBuilder().expectStatusCode(200).build();
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.FALSE);
+ }
+ }
+
+ @Test
+ public void saleIsNotAllowedWhenTransferIsAlreadyPending() {
+ try {
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.TRUE);
+
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
+
GlobalConfigurationHelper.updateValueForGlobalConfiguration(requestSpec,
responseSpec, "10", "0");
+
+ final Integer clientID = ClientHelper.createClient(requestSpec,
responseSpec);
+ Assertions.assertNotNull(clientID);
+
+ Integer overdueFeeChargeId =
ChargesHelper.createCharges(requestSpec, responseSpec,
+
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
+ Assertions.assertNotNull(overdueFeeChargeId);
+
+ final Integer loanProductID =
createLoanProduct(overdueFeeChargeId.toString());
+ Assertions.assertNotNull(loanProductID);
+ HashMap loanStatusHashMap;
+
+ final Integer loanID =
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null,
"10 January 2020");
+
+ Assertions.assertNotNull(loanID);
+
+ loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec,
responseSpec, loanID);
+ LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+ loanStatusHashMap = loanTransactionHelper.approveLoan("01 March
2020", loanID);
+ LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+
+ String loanDetails =
loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
+ loanStatusHashMap =
loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020",
loanID,
+
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+ LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+ String transferExternalId = "36efeb06-d835-48a1-99eb-09bd1d348c1e";
+ String saleResponse =
externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "sale",
+ getSaleRequestJson("04 March 2020", transferExternalId));
+ Type type = new TypeToken<Map<String, Object>>() {}.getType();
+ Map<String, Object> responseMap = new
Gson().fromJson(saleResponse, type);
+ assertEquals(responseMap.get("resourceExternalId"),
transferExternalId);
+
+ externalAssetOwnerHelper = new
ExternalAssetOwnerHelper(requestSpec, responseSpecError);
+ String errorResponse =
externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "sale",
+ getSaleRequestJson("04 March 2020", transferExternalId));
+ Map<String, Object> errorResponseMap = new
Gson().fromJson(errorResponse, type);
+ assertEquals("External asset owner transfer is already in PENDING
state for this loan.",
+ errorResponseMap.get("developerMessage"));
+ } finally {
+ requestSpec = new
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " +
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ requestSpec.header("Fineract-Platform-TenantId", "default");
+ responseSpec = new
ResponseSpecBuilder().expectStatusCode(200).build();
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.FALSE);
+ }
+ }
+
+ @Test
+ public void saleAndBuybackOnTheSameDay() {
+ try {
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.TRUE);
+
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
+
GlobalConfigurationHelper.updateValueForGlobalConfiguration(requestSpec,
responseSpec, "10", "0");
+
+ final Integer clientID = ClientHelper.createClient(requestSpec,
responseSpec);
+ Assertions.assertNotNull(clientID);
+
+ Integer overdueFeeChargeId =
ChargesHelper.createCharges(requestSpec, responseSpec,
+
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
+ Assertions.assertNotNull(overdueFeeChargeId);
+
+ final Integer loanProductID =
createLoanProduct(overdueFeeChargeId.toString());
+ Assertions.assertNotNull(loanProductID);
+ HashMap loanStatusHashMap;
+
+ final Integer loanID =
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null,
"10 January 2020");
+
+ Assertions.assertNotNull(loanID);
+
+ loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec,
responseSpec, loanID);
+ LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+ loanStatusHashMap = loanTransactionHelper.approveLoan("01 March
2020", loanID);
+ LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+
+ String loanDetails =
loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
+ loanStatusHashMap =
loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020",
loanID,
+
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+ LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+ String transferExternalId = "36efeb06-d835-48a1-99eb-09bd1d348c1e";
+ String saleResponse =
externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "sale",
+ getSaleRequestJson("04 March 2020", transferExternalId));
+ Type type = new TypeToken<Map<String, Object>>() {}.getType();
+ Map<String, Object> responseMap = new
Gson().fromJson(saleResponse, type);
+ assertEquals(responseMap.get("resourceExternalId"),
transferExternalId);
+
+ String buybackTransferExternalId =
"36efeb06-d835-48a1-99eb-09bd1d348c1e";
+ String buybackResponse =
externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "buyback",
+ getSaleRequestJson("04 March 2020",
buybackTransferExternalId));
+ Map<String, Object> buybackResponseMap = new
Gson().fromJson(buybackResponse, type);
+ assertEquals(buybackResponseMap.get("resourceExternalId"),
buybackTransferExternalId);
+
+ String retrieveResponse =
externalAssetOwnerHelper.retrieveTransferByLoanId(loanID.longValue());
+ Type retrieveType = new TypeToken<List<Map<String, Object>>>()
{}.getType();
+ List<Map<String, Object>> retrieveResponseMap = new
Gson().fromJson(retrieveResponse, retrieveType);
+ assertEquals(2, retrieveResponseMap.size());
+ assertEquals(retrieveResponseMap.get(0).get("transferExternalId"),
transferExternalId);
+ assertEquals(retrieveResponseMap.get(0).get("status"), "PENDING");
+ assertEquals(retrieveResponseMap.get(1).get("transferExternalId"),
buybackTransferExternalId);
+ assertEquals(retrieveResponseMap.get(1).get("status"), "BUYBACK");
+ } finally {
+ requestSpec = new
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " +
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ requestSpec.header("Fineract-Platform-TenantId", "default");
+ responseSpec = new
ResponseSpecBuilder().expectStatusCode(200).build();
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.FALSE);
+ }
+ }
+
+ private Integer createLoanProduct(final String chargeId) {
+ final String loanProductJSON = new
LoanProductTestBuilder().withPrincipal("15,000.00").withNumberOfRepayments("4")
+
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("1")
+
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
+ .build(chargeId);
+ return loanTransactionHelper.getLoanProductId(loanProductJSON);
+ }
+
+ private Integer applyForLoanApplication(final String clientID, final
String loanProductID, final String savingsID, final String date) {
+
+ List<HashMap> collaterals = new ArrayList<>();
+ final Integer collateralId =
CollateralManagementHelper.createCollateralProduct(requestSpec, responseSpec);
+ Assertions.assertNotNull(collateralId);
+ final Integer clientCollateralId =
CollateralManagementHelper.createClientCollateral(requestSpec, responseSpec,
clientID,
+ collateralId);
+ Assertions.assertNotNull(clientCollateralId);
+ addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
+
+ final String loanApplicationJSON = new
LoanApplicationTestBuilder().withPrincipal("15,000.00").withLoanTermFrequency("4")
+
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("4").withRepaymentEveryAfter("1")
+
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withAmortizationTypeAsEqualInstallments()
+
.withInterestTypeAsDecliningBalance().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
+
.withExpectedDisbursementDate(date).withSubmittedOnDate(date).withCollaterals(collaterals)
+ .build(clientID, loanProductID, savingsID);
+ return loanTransactionHelper.getLoanId(loanApplicationJSON);
+ }
+
+ private void addCollaterals(List<HashMap> collaterals, Integer
collateralId, BigDecimal quantity) {
+ collaterals.add(collaterals(collateralId, quantity));
+ }
+
+ private HashMap<String, String> collaterals(Integer collateralId,
BigDecimal quantity) {
+ HashMap<String, String> collateral = new HashMap<>(2);
+ collateral.put("clientCollateralId", collateralId.toString());
+ collateral.put("quantity", quantity.toString());
+ return collateral;
+ }
+
+ private String getSaleRequestJson(String date, String transferExternalId) {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("settlement_date", date);
+ map.put("owner_external_id", "1234567890987654321");
+ map.put("transfer_external_id", transferExternalId);
+ map.put("purchase_price_ratio", "1.234");
+ map.put("dateformat", "dd MMMM yyyy");
+ map.put("locale", "en");
+ return new Gson().toJson(map);
+ }
+
+}