[FINERACT-1926] POST API for asset externalization
This commit is contained in:
+16
@@ -3622,4 +3622,20 @@ public class CommandWrapperBuilder {
|
|||||||
this.href = "/loans/" + loanId + "/transactions?command=undo-charge-off";
|
this.href = "/loans/" + loanId + "/transactions?command=undo-charge-off";
|
||||||
return this;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.core.data;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LoanIdAndExternalIdData {
|
||||||
|
|
||||||
|
private Long loanId;
|
||||||
|
private ExternalId loanExternalId;
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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.loanaccount.service;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.data.LoanIdAndExternalIdData;
|
||||||
|
|
||||||
|
public interface LoanReadPlatformServiceCommon {
|
||||||
|
|
||||||
|
LoanIdAndExternalIdData getTransferableLoanIdAndExternalId(Long loanId);
|
||||||
|
|
||||||
|
Long getLoanIdByLoanExternalId(String externalId);
|
||||||
|
}
|
||||||
+41
-5
@@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
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.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
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.MediaType;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import lombok.RequiredArgsConstructor;
|
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.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.ApiRequestJsonSerializationSettings;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
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.infrastructure.security.service.PlatformUserRightsContext;
|
||||||
import org.apache.fineract.investor.data.ExternalTransferData;
|
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.investor.service.ExternalAssetOwnersReadService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformServiceCommon;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Path("/v1/external-asset-owners")
|
@Path("/v1/external-asset-owners")
|
||||||
@@ -56,29 +64,40 @@ public class ExternalAssetOwnersApiResource {
|
|||||||
private final ExternalAssetOwnersReadService externalAssetOwnersReadService;
|
private final ExternalAssetOwnersReadService externalAssetOwnersReadService;
|
||||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||||
private final DefaultToApiJsonSerializer<ExternalTransferData> apiJsonSerializerService;
|
private final DefaultToApiJsonSerializer<ExternalTransferData> apiJsonSerializerService;
|
||||||
|
private final DefaultToApiJsonSerializer<ExternalTransferResponseData> postApiJsonSerializerService;
|
||||||
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
|
private final LoanReadPlatformServiceCommon loanReadPlatformService;
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("/transfers/loans/{loanId}")
|
@Path("/transfers/loans/{loanId}")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ 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,
|
public String transferRequestWithLoanId(@PathParam("loanId") final Long loanId,
|
||||||
@QueryParam("command") @Parameter(description = "command") final String commandParam,
|
@QueryParam("command") @Parameter(description = "command") final String commandParam,
|
||||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
platformUserRightsContext.isAuthenticated();
|
platformUserRightsContext.isAuthenticated();
|
||||||
|
return getResult(loanId, apiRequestBodyAsJson, commandParam);
|
||||||
throw new NotImplementedException("Not implemented yet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("/transfers/loans/external-id/{loanExternalId}")
|
@Path("/transfers/loans/external-id/{loanExternalId}")
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ 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,
|
@QueryParam("command") @Parameter(description = "command") final String commandParam,
|
||||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||||
platformUserRightsContext.isAuthenticated();
|
platformUserRightsContext.isAuthenticated();
|
||||||
|
Long loanId = loanReadPlatformService.getLoanIdByLoanExternalId(externalLoanId);
|
||||||
|
|
||||||
throw new NotImplementedException("Not implemented yet");
|
return getResult(loanId, apiRequestBodyAsJson, commandParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -99,4 +118,21 @@ public class ExternalAssetOwnersApiResource {
|
|||||||
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
return apiJsonSerializerService.serialize(settings, transferDataList);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-2
@@ -19,7 +19,6 @@
|
|||||||
package org.apache.fineract.investor.api;
|
package org.apache.fineract.investor.api;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.apache.fineract.investor.data.ExternalTransferStatus;
|
import org.apache.fineract.investor.data.ExternalTransferStatus;
|
||||||
@@ -68,7 +67,7 @@ final class ExternalAssetOwnersApiResourceSwagger {
|
|||||||
public String transferExternalId;
|
public String transferExternalId;
|
||||||
|
|
||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
public BigDecimal purchasePriceRatio;
|
public String purchasePriceRatio;
|
||||||
|
|
||||||
@Schema(example = "[2023, 5, 23]")
|
@Schema(example = "[2023, 5, 23]")
|
||||||
public LocalDate settlementDate;
|
public LocalDate settlementDate;
|
||||||
@@ -87,4 +86,70 @@ final class ExternalAssetOwnersApiResourceSwagger {
|
|||||||
public Integer totalFilteredRecords;
|
public Integer totalFilteredRecords;
|
||||||
public Set<GetExternalTransferPageItems> pageItems;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ExternalTransferChangedData {
|
||||||
|
|
||||||
|
private LocalDate settlementDate;
|
||||||
|
private String transferExternalId;
|
||||||
|
private String ownerExternalId;
|
||||||
|
private String purchasePriceRatio;
|
||||||
|
}
|
||||||
+1
-2
@@ -18,7 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.investor.data;
|
package org.apache.fineract.investor.data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ public class ExternalTransferData {
|
|||||||
private ExternalTransferOwnerData owner;
|
private ExternalTransferOwnerData owner;
|
||||||
private ExternalTransferLoanMappingData loan;
|
private ExternalTransferLoanMappingData loan;
|
||||||
private String transferExternalId;
|
private String transferExternalId;
|
||||||
private BigDecimal purchasePriceRatio;
|
private String purchasePriceRatio;
|
||||||
private LocalDate settlementDate;
|
private LocalDate settlementDate;
|
||||||
private ExternalTransferStatus status;
|
private ExternalTransferStatus status;
|
||||||
private LocalDate effectiveFrom;
|
private LocalDate effectiveFrom;
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
public final class ExternalTransferRequestParameters {
|
||||||
|
|
||||||
|
private ExternalTransferRequestParameters() {}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
+1
-2
@@ -21,7 +21,6 @@ package org.apache.fineract.investor.domain;
|
|||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -30,7 +29,7 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Entity
|
@Entity
|
||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
@NoArgsConstructor
|
||||||
@Table(name = "m_external_asset_owner")
|
@Table(name = "m_external_asset_owner")
|
||||||
public class ExternalAssetOwner extends AbstractAuditableWithUTCDateTimeCustom {
|
public class ExternalAssetOwner extends AbstractAuditableWithUTCDateTimeCustom {
|
||||||
|
|
||||||
|
|||||||
+4
@@ -18,10 +18,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.investor.domain;
|
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.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
|
||||||
public interface ExternalAssetOwnerRepository
|
public interface ExternalAssetOwnerRepository
|
||||||
extends JpaRepository<ExternalAssetOwner, Long>, JpaSpecificationExecutor<ExternalAssetOwner> {
|
extends JpaRepository<ExternalAssetOwner, Long>, JpaSpecificationExecutor<ExternalAssetOwner> {
|
||||||
|
|
||||||
|
Optional<ExternalAssetOwner> findByExternalId(ExternalId externalId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -18,14 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.investor.domain;
|
package org.apache.fineract.investor.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -34,7 +32,7 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Table(name = "m_external_asset_owner_transfer")
|
@Table(name = "m_external_asset_owner_transfer")
|
||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
@NoArgsConstructor
|
||||||
@Entity
|
@Entity
|
||||||
public class ExternalAssetOwnerTransfer extends AbstractAuditableWithUTCDateTimeCustom {
|
public class ExternalAssetOwnerTransfer extends AbstractAuditableWithUTCDateTimeCustom {
|
||||||
|
|
||||||
@@ -51,8 +49,8 @@ public class ExternalAssetOwnerTransfer extends AbstractAuditableWithUTCDateTime
|
|||||||
@Column(name = "status", length = 50)
|
@Column(name = "status", length = 50)
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
@Column(name = "purchase_price_ratio", precision = 19, scale = 6)
|
@Column(name = "purchase_price_ratio", length = 50)
|
||||||
private BigDecimal purchasePriceRatio;
|
private String purchasePriceRatio;
|
||||||
|
|
||||||
@Column(name = "settlement_date")
|
@Column(name = "settlement_date")
|
||||||
private LocalDate settlementDate;
|
private LocalDate settlementDate;
|
||||||
|
|||||||
+10
-3
@@ -19,6 +19,8 @@
|
|||||||
package org.apache.fineract.investor.domain;
|
package org.apache.fineract.investor.domain;
|
||||||
|
|
||||||
import java.util.List;
|
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.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
@@ -27,7 +29,12 @@ import org.springframework.data.repository.query.Param;
|
|||||||
public interface ExternalAssetOwnerTransferRepository
|
public interface ExternalAssetOwnerTransferRepository
|
||||||
extends JpaRepository<ExternalAssetOwnerTransfer, Long>, JpaSpecificationExecutor<ExternalAssetOwnerTransfer> {
|
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> findAllByLoanId(Long loanId);
|
||||||
List<ExternalAssetOwnerTransfer> findAllByIncomingId(@Param("loanId") Long loanId, @Param("loanExternalId") String loanExternalId,
|
|
||||||
@Param("transferExternalId") String transferExternalId);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.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> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToDatabaseColumn(ExternalId externalId) {
|
||||||
|
return externalId != null ? externalId.getValue() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExternalId convertToEntityAttribute(String externalId) {
|
||||||
|
return StringUtils.isBlank(externalId) ? ExternalId.empty() : ExternalIdFactory.produce(externalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class ExternalAssetOwnerInitiateTransferException extends RuntimeException {
|
||||||
|
|
||||||
|
public ExternalAssetOwnerInitiateTransferException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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 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;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "LOAN", action = "BUYBACK")
|
||||||
|
public class BuybackLoanFromExternalAssetOwnerHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final ExternalAssetOwnersWriteService externalAssetOwnersWriteService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(JsonCommand command) {
|
||||||
|
return externalAssetOwnersWriteService.buyBackLoanByLoanId(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-3
@@ -18,8 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.investor.service;
|
package org.apache.fineract.investor.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
import org.apache.fineract.investor.data.ExternalTransferData;
|
import org.apache.fineract.investor.data.ExternalTransferData;
|
||||||
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer;
|
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer;
|
||||||
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
|
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
|
||||||
@@ -33,12 +35,17 @@ public class ExternalAssetOwnersReadServiceImpl implements ExternalAssetOwnersRe
|
|||||||
|
|
||||||
private final ExternalAssetOwnerTransferRepository externalAssetOwnerTransferRepository;
|
private final ExternalAssetOwnerTransferRepository externalAssetOwnerTransferRepository;
|
||||||
private final ExternalAssetOwnersTransferMapper mapper;
|
private final ExternalAssetOwnersTransferMapper mapper;
|
||||||
private final ExternalAssetOwnersTransferMapper externalAssetOwnersTransferMapper;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ExternalTransferData> retrieveTransferData(Long loanId, String externalLoanId, String transferExternalId) {
|
public List<ExternalTransferData> retrieveTransferData(Long loanId, String externalLoanId, String transferExternalId) {
|
||||||
List<ExternalAssetOwnerTransfer> result = externalAssetOwnerTransferRepository.findAllByIncomingId(loanId, externalLoanId,
|
List<ExternalAssetOwnerTransfer> result = new ArrayList<>();
|
||||||
transferExternalId);
|
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();
|
return result.stream().map(mapper::mapTransfer).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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 org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
|
public interface ExternalAssetOwnersWriteService {
|
||||||
|
|
||||||
|
CommandProcessingResult saleLoanByLoanId(JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult buyBackLoanByLoanId(JsonCommand command);
|
||||||
|
}
|
||||||
+215
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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 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;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "LOAN", action = "SALE")
|
||||||
|
public class SaleLoanToExternalAssetOwnerHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final ExternalAssetOwnersWriteService externalAssetOwnersWriteService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(JsonCommand command) {
|
||||||
|
return externalAssetOwnersWriteService.saleLoanByLoanId(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -24,4 +24,7 @@
|
|||||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
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/0001_initial_schema.xml"/>
|
||||||
<include relativeToChangelogFile="true" file="parts/0002_asset_schemas.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>
|
</databaseChangeLog>
|
||||||
|
|||||||
+20
@@ -44,4 +44,24 @@
|
|||||||
|
|
||||||
</changeSet>
|
</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>
|
</databaseChangeLog>
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
<?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 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>
|
||||||
+41
@@ -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>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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.cob.data;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
|
|
||||||
|
public interface LoanIdAndExternalId {
|
||||||
|
|
||||||
|
Long getId();
|
||||||
|
|
||||||
|
ExternalId getExternalId();
|
||||||
|
}
|
||||||
+6
-1
@@ -60,7 +60,12 @@ public class CustomAuditingHandler extends AuditingHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DateTimeProvider fetchDateTimeProvider(Object bean) {
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
-1
@@ -21,6 +21,7 @@ package org.apache.fineract.infrastructure.core.domain;
|
|||||||
import javax.persistence.AttributeConverter;
|
import javax.persistence.AttributeConverter;
|
||||||
import javax.persistence.Converter;
|
import javax.persistence.Converter;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
|
|
||||||
@Converter(autoApply = true)
|
@Converter(autoApply = true)
|
||||||
public class ExternalIdConverter implements AttributeConverter<ExternalId, String> {
|
public class ExternalIdConverter implements AttributeConverter<ExternalId, String> {
|
||||||
@@ -32,7 +33,7 @@ public class ExternalIdConverter implements AttributeConverter<ExternalId, Strin
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExternalId convertToEntityAttribute(String externalId) {
|
public ExternalId convertToEntityAttribute(String externalId) {
|
||||||
return StringUtils.isBlank(externalId) ? ExternalId.empty() : new ExternalId(externalId);
|
return StringUtils.isBlank(externalId) ? ExternalId.empty() : ExternalIdFactory.produce(externalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -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 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";
|
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
|
// 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)
|
@Query(FIND_BY_ACCOUNT_NUMBER)
|
||||||
Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber);
|
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);
|
boolean existsByExternalId(@Param("externalId") ExternalId externalId);
|
||||||
|
|
||||||
@Query(FIND_ALL_NON_CLOSED)
|
@Query(FIND_ALL_NON_CLOSED)
|
||||||
|
|||||||
+4
@@ -262,4 +262,8 @@ public class LoanRepositoryWrapper {
|
|||||||
public List<Long> findLoanIdsByStatusId(Integer statusId) {
|
public List<Long> findLoanIdsByStatusId(Integer statusId) {
|
||||||
return repository.findLoanIdByStatusId(statusId);
|
return repository.findLoanIdByStatusId(statusId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Loan getNonClosedLoanIdAndExternalIdByLoanId(Long loanId) {
|
||||||
|
return repository.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -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.codes.service.CodeValueReadPlatformService;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
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.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
@@ -144,7 +145,7 @@ import org.springframework.util.CollectionUtils;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Service
|
@Service
|
||||||
@Transactional(readOnly = true)
|
@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 static final String ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE = "submitted-date";
|
||||||
private final JdbcTemplate jdbcTemplate;
|
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 static final class LoanMapper implements RowMapper<LoanAccountData> {
|
||||||
|
|
||||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||||
|
|||||||
+53
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+300
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user