[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";
|
||||
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.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -37,13 +38,20 @@ import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformUserRightsContext;
|
||||
import org.apache.fineract.investor.data.ExternalTransferData;
|
||||
import org.apache.fineract.investor.data.ExternalTransferResponseData;
|
||||
import org.apache.fineract.investor.service.ExternalAssetOwnersReadService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformServiceCommon;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/external-asset-owners")
|
||||
@@ -56,29 +64,40 @@ public class ExternalAssetOwnersApiResource {
|
||||
private final ExternalAssetOwnersReadService externalAssetOwnersReadService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final DefaultToApiJsonSerializer<ExternalTransferData> apiJsonSerializerService;
|
||||
private final DefaultToApiJsonSerializer<ExternalTransferResponseData> postApiJsonSerializerService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final LoanReadPlatformServiceCommon loanReadPlatformService;
|
||||
|
||||
@POST
|
||||
@Path("/transfers/loans/{loanId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferResponse.class))),
|
||||
@ApiResponse(responseCode = "403", description = "Transfer cannot be initiated") })
|
||||
public String transferRequestWithLoanId(@PathParam("loanId") final Long loanId,
|
||||
@QueryParam("command") @Parameter(description = "command") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
platformUserRightsContext.isAuthenticated();
|
||||
|
||||
throw new NotImplementedException("Not implemented yet");
|
||||
return getResult(loanId, apiRequestBodyAsJson, commandParam);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/transfers/loans/external-id/{loanExternalId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String transferRequestWithLoanExternalId(@PathParam("loanExternalId") final Long loanId,
|
||||
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferRequest.class)))
|
||||
@ApiResponses({
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ExternalAssetOwnersApiResourceSwagger.PostInitiateTransferResponse.class))),
|
||||
@ApiResponse(responseCode = "403", description = "Transfer cannot be initiated") })
|
||||
public String transferRequestWithLoanExternalId(@PathParam("loanExternalId") final String externalLoanId,
|
||||
@QueryParam("command") @Parameter(description = "command") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
platformUserRightsContext.isAuthenticated();
|
||||
Long loanId = loanReadPlatformService.getLoanIdByLoanExternalId(externalLoanId);
|
||||
|
||||
throw new NotImplementedException("Not implemented yet");
|
||||
return getResult(loanId, apiRequestBodyAsJson, commandParam);
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -99,4 +118,21 @@ public class ExternalAssetOwnersApiResource {
|
||||
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return apiJsonSerializerService.serialize(settings, transferDataList);
|
||||
}
|
||||
|
||||
private String getResult(Long loanId, String apiRequestBodyAsJson, String commandParam) {
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
CommandWrapper commandRequest = null;
|
||||
if (CommandParameterUtil.is(commandParam, "sale")) {
|
||||
commandRequest = builder.saleLoanToExternalAssetOwner(loanId).build();
|
||||
} else if (CommandParameterUtil.is(commandParam, "buyback")) {
|
||||
commandRequest = builder.buybackLoanToExternalAssetOwner(loanId).build();
|
||||
}
|
||||
|
||||
if (commandRequest == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return postApiJsonSerializerService.serialize(result);
|
||||
}
|
||||
}
|
||||
|
||||
+67
-2
@@ -19,7 +19,6 @@
|
||||
package org.apache.fineract.investor.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.investor.data.ExternalTransferStatus;
|
||||
@@ -68,7 +67,7 @@ final class ExternalAssetOwnersApiResourceSwagger {
|
||||
public String transferExternalId;
|
||||
|
||||
@Schema(example = "1")
|
||||
public BigDecimal purchasePriceRatio;
|
||||
public String purchasePriceRatio;
|
||||
|
||||
@Schema(example = "[2023, 5, 23]")
|
||||
public LocalDate settlementDate;
|
||||
@@ -87,4 +86,70 @@ final class ExternalAssetOwnersApiResourceSwagger {
|
||||
public Integer totalFilteredRecords;
|
||||
public Set<GetExternalTransferPageItems> pageItems;
|
||||
}
|
||||
|
||||
@Schema(description = "PostInitiateTransferRequest")
|
||||
public static final class PostInitiateTransferRequest {
|
||||
|
||||
private PostInitiateTransferRequest() {}
|
||||
|
||||
@Schema(example = "[2023, 5, 23]")
|
||||
public LocalDate settlementDate;
|
||||
|
||||
@Schema(example = "1234567890987654321abc")
|
||||
public String ownerExternalId;
|
||||
|
||||
@Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c1e")
|
||||
public String transferExternalId;
|
||||
|
||||
@Schema(example = "1.2345678")
|
||||
public String purchasePriceRatio;
|
||||
|
||||
@Schema(example = "yyyy-MM-dd")
|
||||
public String dateformat;
|
||||
|
||||
@Schema(example = "en")
|
||||
public String locale;
|
||||
}
|
||||
|
||||
@Schema(description = "PostInitiateTransferResponse")
|
||||
public static final class PostInitiateTransferResponse {
|
||||
|
||||
private PostInitiateTransferResponse() {}
|
||||
|
||||
@Schema(example = "1", description = "transfer ID")
|
||||
public Long resourceId;
|
||||
|
||||
@Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c1e", description = "transfer external ID")
|
||||
public String resourceExternalId;
|
||||
|
||||
@Schema(example = "2", description = "loan ID")
|
||||
public Long subResourceId;
|
||||
|
||||
@Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c2e", description = "loan external ID")
|
||||
public String subResourceExternalId;
|
||||
|
||||
public ExternalAssetOwnerTransferChangesData changes;
|
||||
|
||||
@Schema(example = "yyyy-MM-dd")
|
||||
public String dateformat;
|
||||
|
||||
@Schema(example = "en")
|
||||
public String locale;
|
||||
|
||||
@Schema(description = "ExternalAssetOwnerTransferChangesData")
|
||||
static final class ExternalAssetOwnerTransferChangesData {
|
||||
|
||||
@Schema(example = "[2023, 5, 23]")
|
||||
public String settlementDate;
|
||||
|
||||
@Schema(example = "1234567890987654321abc")
|
||||
public String ownerExternalId;
|
||||
|
||||
@Schema(example = "36efeb06-d835-48a1-99eb-09bd1d348c1e")
|
||||
public String transferExternalId;
|
||||
|
||||
@Schema(example = "1.23456789")
|
||||
public String purchasePriceRatio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -29,7 +28,7 @@ public class ExternalTransferData {
|
||||
private ExternalTransferOwnerData owner;
|
||||
private ExternalTransferLoanMappingData loan;
|
||||
private String transferExternalId;
|
||||
private BigDecimal purchasePriceRatio;
|
||||
private String purchasePriceRatio;
|
||||
private LocalDate settlementDate;
|
||||
private ExternalTransferStatus status;
|
||||
private LocalDate effectiveFrom;
|
||||
|
||||
+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.Entity;
|
||||
import javax.persistence.Table;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
@@ -30,7 +29,7 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@NoArgsConstructor
|
||||
@Table(name = "m_external_asset_owner")
|
||||
public class ExternalAssetOwner extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
|
||||
+4
@@ -18,10 +18,14 @@
|
||||
*/
|
||||
package org.apache.fineract.investor.domain;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface ExternalAssetOwnerRepository
|
||||
extends JpaRepository<ExternalAssetOwner, Long>, JpaSpecificationExecutor<ExternalAssetOwner> {
|
||||
|
||||
Optional<ExternalAssetOwner> findByExternalId(ExternalId externalId);
|
||||
|
||||
}
|
||||
|
||||
+3
-5
@@ -18,14 +18,12 @@
|
||||
*/
|
||||
package org.apache.fineract.investor.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
@@ -34,7 +32,7 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
@Getter
|
||||
@Setter
|
||||
@Table(name = "m_external_asset_owner_transfer")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class ExternalAssetOwnerTransfer extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
@@ -51,8 +49,8 @@ public class ExternalAssetOwnerTransfer extends AbstractAuditableWithUTCDateTime
|
||||
@Column(name = "status", length = 50)
|
||||
private String status;
|
||||
|
||||
@Column(name = "purchase_price_ratio", precision = 19, scale = 6)
|
||||
private BigDecimal purchasePriceRatio;
|
||||
@Column(name = "purchase_price_ratio", length = 50)
|
||||
private String purchasePriceRatio;
|
||||
|
||||
@Column(name = "settlement_date")
|
||||
private LocalDate settlementDate;
|
||||
|
||||
+10
-3
@@ -19,6 +19,8 @@
|
||||
package org.apache.fineract.investor.domain;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
@@ -27,7 +29,12 @@ import org.springframework.data.repository.query.Param;
|
||||
public interface ExternalAssetOwnerTransferRepository
|
||||
extends JpaRepository<ExternalAssetOwnerTransfer, Long>, JpaSpecificationExecutor<ExternalAssetOwnerTransfer> {
|
||||
|
||||
@Query("select e from ExternalAssetOwnerTransfer e where (:loanId is null or e.loanId = :loanId) and (:loanExternalId is null or e.externalLoanId = :loanExternalId) and (:transferExternalId is null or e.externalId = :transferExternalId)")
|
||||
List<ExternalAssetOwnerTransfer> findAllByIncomingId(@Param("loanId") Long loanId, @Param("loanExternalId") String loanExternalId,
|
||||
@Param("transferExternalId") String transferExternalId);
|
||||
List<ExternalAssetOwnerTransfer> findAllByLoanId(Long loanId);
|
||||
|
||||
List<ExternalAssetOwnerTransfer> findAllByExternalLoanId(ExternalId externalLoanId);
|
||||
|
||||
List<ExternalAssetOwnerTransfer> findAllByExternalId(ExternalId externalId);
|
||||
|
||||
@Query("select e from ExternalAssetOwnerTransfer e where e.loanId = :loanId and e.id = (select max(ex.id) from ExternalAssetOwnerTransfer ex where ex.loanId = :loanId)")
|
||||
Optional<ExternalAssetOwnerTransfer> findLatestByLoanId(@Param("loanId") Long loanId);
|
||||
}
|
||||
|
||||
+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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.investor.data.ExternalTransferData;
|
||||
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer;
|
||||
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
|
||||
@@ -33,12 +35,17 @@ public class ExternalAssetOwnersReadServiceImpl implements ExternalAssetOwnersRe
|
||||
|
||||
private final ExternalAssetOwnerTransferRepository externalAssetOwnerTransferRepository;
|
||||
private final ExternalAssetOwnersTransferMapper mapper;
|
||||
private final ExternalAssetOwnersTransferMapper externalAssetOwnersTransferMapper;
|
||||
|
||||
@Override
|
||||
public List<ExternalTransferData> retrieveTransferData(Long loanId, String externalLoanId, String transferExternalId) {
|
||||
List<ExternalAssetOwnerTransfer> result = externalAssetOwnerTransferRepository.findAllByIncomingId(loanId, externalLoanId,
|
||||
transferExternalId);
|
||||
List<ExternalAssetOwnerTransfer> result = new ArrayList<>();
|
||||
if (loanId != null) {
|
||||
result.addAll(externalAssetOwnerTransferRepository.findAllByLoanId(loanId));
|
||||
} else if (externalLoanId != null) {
|
||||
result.addAll(externalAssetOwnerTransferRepository.findAllByExternalLoanId(ExternalIdFactory.produce(externalLoanId)));
|
||||
} else if (transferExternalId != null) {
|
||||
result.addAll(externalAssetOwnerTransferRepository.findAllByExternalId(ExternalIdFactory.produce(transferExternalId)));
|
||||
}
|
||||
return result.stream().map(mapper::mapTransfer).toList();
|
||||
}
|
||||
}
|
||||
|
||||
+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">
|
||||
<include relativeToChangelogFile="true" file="parts/0001_initial_schema.xml"/>
|
||||
<include relativeToChangelogFile="true" file="parts/0002_asset_schemas.xml"/>
|
||||
<include relativeToChangelogFile="true" file="parts/0003_asset_schemas.xml"/>
|
||||
<include relativeToChangelogFile="true" file="parts/0004_change_purchase_price_ratio_type.xml"/>
|
||||
<include relativeToChangelogFile="true" file="parts/0005_add_sale_and_buyback_command.xml"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+20
@@ -44,4 +44,24 @@
|
||||
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="3" context="mysql">
|
||||
|
||||
<addColumn tableName="m_external_asset_owner_transfer">
|
||||
<column name="external_loan_id" type="varchar(100)"/>
|
||||
<column name="loan_id" type="bigint" remarks="Loan ID">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
<changeSet author="fineract" id="4" context="mysql">
|
||||
|
||||
<addForeignKeyConstraint baseColumnNames="loan_id"
|
||||
baseTableName="m_external_asset_owner_transfer"
|
||||
constraintName="FK_external_asset_owner_transfer_loan_id" deferrable="false"
|
||||
initiallyDeferred="false"
|
||||
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
|
||||
referencedTableName="m_loan" validate="true"/>
|
||||
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
|
||||
+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) {
|
||||
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.Converter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
|
||||
@Converter(autoApply = true)
|
||||
public class ExternalIdConverter implements AttributeConverter<ExternalId, String> {
|
||||
@@ -32,7 +33,7 @@ public class ExternalIdConverter implements AttributeConverter<ExternalId, Strin
|
||||
|
||||
@Override
|
||||
public ExternalId convertToEntityAttribute(String externalId) {
|
||||
return StringUtils.isBlank(externalId) ? ExternalId.empty() : new ExternalId(externalId);
|
||||
return StringUtils.isBlank(externalId) ? ExternalId.empty() : ExternalIdFactory.produce(externalId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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 GET_NON_CLOSED_LOAN_BY_LOAN_ID = "select loan from Loan loan where loan.id = :loanId and loan.loanStatus in (100,200,300,"
|
||||
+ "303,304)";
|
||||
|
||||
String EXISTS_NON_CLOSED_BY_EXTERNAL_LOAN_ID = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.externalId = :externalLoanId and loan.loanStatus in (100,200,300,303,304)";
|
||||
|
||||
String FIND_ID_BY_EXTERNAL_ID = "SELECT loan.id FROM Loan loan WHERE loan.externalId = :externalId";
|
||||
|
||||
// should follow the logic of `FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE` query
|
||||
@@ -191,6 +196,12 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
@Query(FIND_BY_ACCOUNT_NUMBER)
|
||||
Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber);
|
||||
|
||||
@Query(GET_NON_CLOSED_LOAN_BY_LOAN_ID)
|
||||
Loan getNonClosedLoanIdAndExternalIdByLoanId(@Param("loanId") Long loanId);
|
||||
|
||||
@Query(EXISTS_NON_CLOSED_BY_EXTERNAL_LOAN_ID)
|
||||
boolean existsNonClosedLoanByExternalLoanId(@Param("externalLoanId") ExternalId externalLoanId);
|
||||
|
||||
boolean existsByExternalId(@Param("externalId") ExternalId externalId);
|
||||
|
||||
@Query(FIND_ALL_NON_CLOSED)
|
||||
|
||||
+4
@@ -262,4 +262,8 @@ public class LoanRepositoryWrapper {
|
||||
public List<Long> findLoanIdsByStatusId(Integer statusId) {
|
||||
return repository.findLoanIdByStatusId(statusId);
|
||||
}
|
||||
|
||||
public Loan getNonClosedLoanIdAndExternalIdByLoanId(Long loanId) {
|
||||
return repository.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
|
||||
}
|
||||
}
|
||||
|
||||
+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.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.data.LoanIdAndExternalIdData;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
@@ -144,7 +145,7 @@ import org.springframework.util.CollectionUtils;
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, LoanReadPlatformServiceCommon {
|
||||
|
||||
private static final String ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE = "submitted-date";
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
@@ -591,6 +592,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanIdAndExternalIdData getTransferableLoanIdAndExternalId(Long loanId) {
|
||||
Loan loan = loanRepositoryWrapper.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
|
||||
return new LoanIdAndExternalIdData(loan.getId(), loan.getExternalId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLoanIdByLoanExternalId(String externalId) {
|
||||
return loanRepositoryWrapper.findIdByExternalId(ExternalIdFactory.produce(externalId));
|
||||
}
|
||||
|
||||
private static final class LoanMapper implements RowMapper<LoanAccountData> {
|
||||
|
||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||
|
||||
+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