FINERACT-2181: Fix the interest calculation for non active loan

This commit is contained in:
Attila Budai
2025-07-31 19:59:15 +02:00
committed by Adam Saghy
parent 3e2684c0d6
commit 0762a012e5
13 changed files with 517 additions and 21 deletions
@@ -49,6 +49,11 @@ public class ExternalAssetOwnerTransferOutstandingInterestCalculation {
}
public BigDecimal calculateOutstandingInterest(Loan loan) {
// If loan is not active, there should be no outstanding interest
if (!loan.isOpen()) {
return BigDecimal.ZERO;
}
String outstandingInterestCalculationStrategy = configurationDomainService.getAssetOwnerTransferOustandingInterestStrategy();
return switch (outstandingInterestCalculationStrategy) {
case "TOTAL_OUTSTANDING_INTEREST" -> loan.getSummary().getTotalInterestOutstanding();
@@ -21,11 +21,6 @@ package org.apache.fineract.investor.service;
import static org.apache.fineract.investor.data.ExternalTransferStatus.ACTIVE_INTERMEDIATE;
import static org.apache.fineract.investor.data.ExternalTransferStatus.PENDING;
import static org.apache.fineract.investor.data.ExternalTransferStatus.PENDING_INTERMEDIATE;
import static org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.ACTIVE;
import static org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.CLOSED_OBLIGATIONS_MET;
import static org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.OVERPAID;
import static org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.TRANSFER_IN_PROGRESS;
import static org.apache.fineract.portfolio.loanaccount.domain.LoanStatus.TRANSFER_ON_HOLD;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@@ -43,6 +38,7 @@ import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.cob.data.LoanDataForExternalTransfer;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
@@ -74,9 +70,6 @@ import org.springframework.transaction.annotation.Transactional;
public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersWriteService {
private static final LocalDate FUTURE_DATE_9999_12_31 = LocalDate.of(9999, 12, 31);
private static final List<LoanStatus> ACTIVE_LOAN_STATUSES = List.of(ACTIVE, TRANSFER_IN_PROGRESS, TRANSFER_ON_HOLD);
private static final List<LoanStatus> VALID_DELAYED_SETTLEMENT_LOAN_STATUSES_BUYBACK_AND_SALE = List.of(ACTIVE, TRANSFER_IN_PROGRESS,
TRANSFER_ON_HOLD, OVERPAID, CLOSED_OBLIGATIONS_MET);
private static final List<ExternalTransferStatus> BUYBACK_READY_STATUSES = List.of(ExternalTransferStatus.PENDING,
ExternalTransferStatus.ACTIVE);
private static final List<ExternalTransferStatus> BUYBACK_READY_STATUSES_FOR_DELAY_SETTLEMENT = List
@@ -86,6 +79,7 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
private final FromJsonHelper fromApiJsonHelper;
private final LoanRepository loanRepository;
private final DelayedSettlementAttributeService delayedSettlementAttributeService;
private final ConfigurationDomainService configurationDomainService;
@Override
@Transactional
@@ -388,16 +382,16 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
private void validateLoanStatusIntermediarySale(LoanDataForExternalTransfer loanDataForExternalTransfer) {
LoanStatus loanStatus = loanDataForExternalTransfer.getLoanStatus();
if (!ACTIVE_LOAN_STATUSES.contains(loanStatus)) {
if (!getAllowedLoanStatuses().contains(loanStatus)) {
throw new ExternalAssetOwnerInitiateTransferException(String.format("Loan status %s is not valid for transfer.", loanStatus));
}
}
private List<LoanStatus> getValidLoanStatusList(boolean isDelayedSettlementEnabled) {
if (isDelayedSettlementEnabled) {
return VALID_DELAYED_SETTLEMENT_LOAN_STATUSES_BUYBACK_AND_SALE;
return getAllowedLoanStatusesForDelayedSettlement();
} else {
return ACTIVE_LOAN_STATUSES;
return getAllowedLoanStatuses();
}
}
@@ -577,4 +571,14 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
externalAssetOwner.setExternalId(ExternalIdFactory.produce(externalId));
return externalAssetOwnerRepository.saveAndFlush(externalAssetOwner);
}
private List<LoanStatus> getAllowedLoanStatuses() {
return configurationDomainService.getAllowedLoanStatusesForExternalAssetTransfer().stream().map(LoanStatus::valueOf)
.collect(Collectors.toList());
}
private List<LoanStatus> getAllowedLoanStatusesForDelayedSettlement() {
return configurationDomainService.getAllowedLoanStatusesOfDelayedSettlementForExternalAssetTransfer().stream()
.map(LoanStatus::valueOf).collect(Collectors.toList());
}
}
@@ -40,4 +40,5 @@
<include relativeToChangelogFile="true" file="parts/0016_add_external_reference_id.xml"/>
<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"/>
</databaseChangeLog>
@@ -0,0 +1,47 @@
<?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
https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="fineract" id="1">
<insert tableName="c_configuration">
<column name="name" value="allowed-loan-statuses-for-external-asset-transfer"/>
<column name="value"/>
<column name="date_value"/>
<column name="string_value" value="ACTIVE,TRANSFER_IN_PROGRESS,TRANSFER_ON_HOLD"/>
<column name="enabled" valueBoolean="true"/>
<column name="is_trap_door" valueBoolean="false"/>
<column name="description" value=" Available options: Any combination from LoanStatus enum separated by comma."/>
</insert>
<insert tableName="c_configuration">
<column name="name" value="allowed-loan-statuses-of-delayed-settlement-for-external-asset-transfer"/>
<column name="value"/>
<column name="date_value"/>
<column name="string_value" value="ACTIVE,TRANSFER_IN_PROGRESS,TRANSFER_ON_HOLD,OVERPAID,CLOSED_OBLIGATIONS_MET"/>
<column name="enabled" valueBoolean="true"/>
<column name="is_trap_door" valueBoolean="false"/>
<column name="description" value=" Available options: Any combination from LoanStatus enum separated by comma."/>
</insert>
</changeSet>
</databaseChangeLog>