FINERACT-1926: Add previous owner to ExternalAssetOwnerTransfer

This commit is contained in:
Oleksii Novikov
2025-08-21 13:50:34 +02:00
committed by Adam Saghy
parent f1c28792f3
commit 4786139882
12 changed files with 83 additions and 0 deletions
@@ -55,6 +55,14 @@
"string"
]
},
{
"default": null,
"name": "previousOwnerExternalId",
"type": [
"null",
"string"
]
},
{
"default": null,
"name": "currency",
@@ -279,6 +279,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
newExternalAssetOwnerTransfer.setPurchasePriceRatio(externalAssetOwnerTransfer.getPurchasePriceRatio());
newExternalAssetOwnerTransfer.setEffectiveDateFrom(effectiveDateFrom);
newExternalAssetOwnerTransfer.setEffectiveDateTo(effectiveDateTo);
newExternalAssetOwnerTransfer.setPreviousOwner(externalAssetOwnerTransfer.getPreviousOwner());
expireTransfer(settlementDate, externalAssetOwnerTransfer);
@@ -26,6 +26,7 @@ public class ExternalTransferData {
private Long transferId;
private ExternalTransferOwnerData owner;
private ExternalTransferOwnerData previousOwner;
private ExternalTransferLoanData loan;
private ExternalTransferDataDetails details;
private String transferExternalId;
@@ -81,4 +81,8 @@ public class ExternalAssetOwnerTransfer extends AbstractAuditableWithUTCDateTime
@Column(name = "external_group_id", length = 100)
private ExternalId externalGroupId;
@ManyToOne
@JoinColumn(name = "previous_owner_id")
private ExternalAssetOwner previousOwner;
}
@@ -43,6 +43,7 @@ public interface ExternalAssetOwnersTransferMapper {
@Mapping(target = "purchasePriceRatio", source = "purchasePriceRatio")
@Mapping(target = "settlementDate", source = "settlementDate")
@Mapping(target = "status", source = "status")
@Mapping(target = "previousOwner", source = "previousOwner")
@Mapping(target = "details", source = "externalAssetOwnerTransferDetails")
ExternalTransferData mapTransfer(ExternalAssetOwnerTransfer source);
@@ -51,6 +51,7 @@ import org.apache.fineract.infrastructure.core.serialization.JsonParserHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.investor.data.ExternalTransferData;
import org.apache.fineract.investor.data.ExternalTransferRequestParameters;
import org.apache.fineract.investor.data.ExternalTransferStatus;
import org.apache.fineract.investor.data.ExternalTransferSubStatus;
@@ -80,6 +81,7 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
private final LoanRepository loanRepository;
private final DelayedSettlementAttributeService delayedSettlementAttributeService;
private final ConfigurationDomainService configurationDomainService;
private final ExternalAssetOwnersReadService externalAssetOwnersReadService;
@Override
@Transactional
@@ -307,6 +309,7 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
externalAssetOwnerTransfer.setEffectiveDateFrom(effectiveDateFrom);
externalAssetOwnerTransfer.setEffectiveDateTo(FUTURE_DATE_9999_12_31);
externalAssetOwnerTransfer.setPurchasePriceRatio(effectiveTransfer.getPurchasePriceRatio());
externalAssetOwnerTransfer.setPreviousOwner(effectiveTransfer.getOwner());
return externalAssetOwnerTransfer;
}
@@ -410,6 +413,9 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
externalAssetOwnerTransfer.setLoanId(loanId);
externalAssetOwnerTransfer.setExternalLoanId(externalLoanId);
externalAssetOwnerTransfer.setExternalGroupId(getTransferExternalGroupIdFromJson(json));
findPreviousAssetOwner(loanId).ifPresent(externalAssetOwnerTransfer::setPreviousOwner);
return externalAssetOwnerTransfer;
}
@@ -428,9 +434,23 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
externalAssetOwnerTransfer.setLoanId(loanId);
externalAssetOwnerTransfer.setExternalLoanId(externalLoanId);
externalAssetOwnerTransfer.setExternalGroupId(getTransferExternalGroupIdFromJson(json));
findPreviousAssetOwner(loanId).ifPresent(externalAssetOwnerTransfer::setPreviousOwner);
return externalAssetOwnerTransfer;
}
private Optional<ExternalAssetOwner> findPreviousAssetOwner(final Long loanId) {
final ExternalTransferData activeTransfer = externalAssetOwnersReadService.retrieveActiveTransferData(loanId, null, null);
if (activeTransfer != null && activeTransfer.getOwner() != null) {
final String activeOwnerExternalId = activeTransfer.getOwner().getExternalId();
return externalAssetOwnerRepository.findByExternalId(ExternalIdFactory.produce(activeOwnerExternalId));
}
return Optional.empty();
}
private void validateSaleRequestBody(String apiRequestBodyAsJson) {
final Set<String> requestParameters = new HashSet<>(Arrays.asList(ExternalTransferRequestParameters.SETTLEMENT_DATE,
ExternalTransferRequestParameters.OWNER_EXTERNAL_ID, ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID,
@@ -39,6 +39,7 @@ public interface ExternalAssetOwnerSearchDataMapper {
@Mapping(target = "status", source = "source", qualifiedByName = "toStatus")
@Mapping(target = "subStatus", source = "source", qualifiedByName = "toSubStatus")
@Mapping(target = "details", source = "source", qualifiedByName = "toDetails")
@Mapping(target = "previousOwner", ignore = true)
ExternalTransferData map(SearchedExternalAssetOwner source);
@Named("toTransferExternalId")
@@ -97,6 +97,8 @@ public class InvestorBusinessEventSerializer extends AbstractBusinessEventWithCu
LoanOwnershipTransferDataV1.Builder builder = LoanOwnershipTransferDataV1.newBuilder().setLoanId(transferData.getLoan().getLoanId())
.setLoanExternalId(transferData.getLoan().getExternalId()).setTransferExternalId(transferData.getTransferExternalId())
.setAssetOwnerExternalId(transferData.getOwner().getExternalId())
.setPreviousOwnerExternalId(
transferData.getPreviousOwner() != null ? transferData.getPreviousOwner().getExternalId() : null)
.setTransferExternalGroupId(transferData.getTransferExternalGroupId())
.setPurchasePriceRatio(transferData.getPurchasePriceRatio()).setCurrency(getCurrencyFromEvent(event))
.setSettlementDate(transferData.getSettlementDate().format(DEFAULT_DATE_FORMATTER))
@@ -41,4 +41,5 @@
<include relativeToChangelogFile="true" file="parts/0017_add_external_asset_owner_loan_product_attr_index.xml"/>
<include relativeToChangelogFile="true" file="parts/0018_add_external_asset_owner_transfer_outstanding_interest_strategy.xml"/>
<include relativeToChangelogFile="true" file="parts/0019_add_configurable_allowed_loan_statuses.xml"/>
<include relativeToChangelogFile="true" file="parts/0020_add_previous_owner_reference.xml"/>
</databaseChangeLog>
@@ -0,0 +1,38 @@
<?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">
<addColumn tableName="m_external_asset_owner_transfer">
<column name="previous_owner_id" type="BIGINT">
<constraints nullable="true"/>
</column>
</addColumn>
<addForeignKeyConstraint baseTableName="m_external_asset_owner_transfer"
baseColumnNames="previous_owner_id"
constraintName="FK_external_asset_owner_transfer_previous_owner"
referencedTableName="m_external_asset_owner"
referencedColumnNames="id"/>
</changeSet>
</databaseChangeLog>
@@ -864,6 +864,9 @@ public class ExternalAssetOwnersWriteServiceTest {
@Mock
private ConfigurationDomainService configurationDomainService;
@Mock
private ExternalAssetOwnersReadService externalAssetOwnersReadService;
@InjectMocks
private ExternalAssetOwnersWriteServiceImpl externalAssetOwnersWriteServiceImpl;
@@ -934,6 +937,7 @@ public class ExternalAssetOwnersWriteServiceTest {
.thenReturn(List.of("ACTIVE", "TRANSFER_IN_PROGRESS", "TRANSFER_ON_HOLD"));
lenient().when(configurationDomainService.getAllowedLoanStatusesOfDelayedSettlementForExternalAssetTransfer())
.thenReturn(List.of("ACTIVE", "TRANSFER_IN_PROGRESS", "TRANSFER_ON_HOLD", "OVERPAID", "CLOSED_OBLIGATIONS_MET"));
lenient().when(externalAssetOwnersReadService.retrieveActiveTransferData(any(Long.class), any(), any())).thenReturn(null);
}
}
@@ -183,6 +183,7 @@ public class InvestorBusinessEventSerializerTest {
assertEquals(1, result.getCurrency().getInMultiplesOf());
assertEquals("1.0", result.getPurchasePriceRatio());
assertEquals(ASSET_OWNER_EXTERNAL_ID, result.getAssetOwnerExternalId());
assertEquals("previous-owner-123", result.getPreviousOwnerExternalId());
assertEquals(LOAN_EXTERNAL_ID, result.getLoanExternalId());
assertEquals(TRANSFER_EXTERNAL_ID.getValue(), result.getTransferExternalId());
assertEquals(LOAN_ID, result.getLoanId());
@@ -232,6 +233,7 @@ public class InvestorBusinessEventSerializerTest {
ExternalTransferData data = new ExternalTransferData();
data.setOwner(new ExternalTransferOwnerData(ASSET_OWNER_EXTERNAL_ID));
data.setPreviousOwner(new ExternalTransferOwnerData("previous-owner-123"));
data.setStatus(status);
data.setSubStatus(subStatus);
data.setTransferId(123L);