FINERACT-2398: Re-amortization Interest Handling configuration
This commit is contained in:
committed by
Adam Saghy
parent
8a86759773
commit
a849e9bae3
+2
@@ -699,6 +699,8 @@ public class LoanTransactionsApiResource {
|
||||
transactionData = this.loanReadPlatformService.retrieveManualInterestRefundTemplate(resolvedLoanId, transactionId);
|
||||
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.REAGE_COMMAND)) {
|
||||
transactionData = this.loanReadPlatformService.retrieveLoanReAgeTemplate(resolvedLoanId);
|
||||
} else if (CommandParameterUtil.is(commandParam, LoanApiConstants.REAMORTIZATION_COMMAND)) {
|
||||
transactionData = this.loanReadPlatformService.retrieveLoanReAmortizationTemplate(resolvedLoanId);
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
|
||||
+14
@@ -132,6 +132,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepayment
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
@@ -2117,6 +2118,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
.reAgeInterestHandlingOptions(LoanReAgeInterestHandlingType.getValuesAsEnumOptionDataList()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionData retrieveLoanReAmortizationTemplate(final Long loanId) {
|
||||
final LoanAccountData loan = this.retrieveOne(loanId);
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REAMORTIZE);
|
||||
final BigDecimal totalOutstanding = loan.getSummary() != null ? loan.getSummary().getTotalOutstanding() : null;
|
||||
final List<CodeValueData> reAmortizationReasonOptions = new ArrayList<>(
|
||||
codeValueReadPlatformService.retrieveCodeValuesByCode(LoanApiConstants.REAMORTIZATION_REASONS));
|
||||
return LoanTransactionData.builder().type(transactionType).currency(loan.getCurrency()).date(DateUtils.getBusinessLocalDate())
|
||||
.amount(totalOutstanding).netDisbursalAmount(loan.getNetDisbursalAmount()).loanId(loanId)
|
||||
.externalLoanId(loan.getExternalId()).reAmortizationReasonOptions(reAmortizationReasonOptions)
|
||||
.reAmortizationInterestHandlingOptions(LoanReAmortizationInterestHandlingType.getValuesAsEnumOptionDataList()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionData retrieveLoanChargeOffTemplate(final Long loanId) {
|
||||
|
||||
|
||||
+24
@@ -26,6 +26,8 @@ import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
@@ -38,12 +40,15 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.reamortization.LoanUndoReAmortizeTransactionBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
@@ -66,6 +71,7 @@ public class LoanReAmortizationServiceImpl {
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final CodeValueRepository codeValueRepository;
|
||||
|
||||
public CommandProcessingResult reAmortize(Long loanId, JsonCommand command) {
|
||||
Loan loan = loanAssembler.assembleFrom(loanId);
|
||||
@@ -82,6 +88,8 @@ public class LoanReAmortizationServiceImpl {
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAmortizeTransaction, new TransactionCtx(loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
reAmortizeTransaction.setLoanReAmortizationParameter(createReAmortizationParameter(reAmortizeTransaction, command));
|
||||
|
||||
loanTransactionRepository.saveAndFlush(reAmortizeTransaction);
|
||||
|
||||
// delinquency recalculation will be triggered by the event in a decoupled way via a listener
|
||||
@@ -159,4 +167,20 @@ public class LoanReAmortizationServiceImpl {
|
||||
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.REAMORTIZE, transactionDate, txPrincipalAmount,
|
||||
txPrincipalAmount, ZERO, ZERO, ZERO, null, false, null, txExternalId);
|
||||
}
|
||||
|
||||
private LoanReAmortizationParameter createReAmortizationParameter(LoanTransaction reAmortizationTransaction, JsonCommand command) {
|
||||
LoanReAmortizationInterestHandlingType reAmortizationInterestHandlingType = command.enumValueOfParameterNamed(
|
||||
LoanReAmortizationApiConstants.reAmortizationInterestHandlingParamName, LoanReAmortizationInterestHandlingType.class);
|
||||
if (reAmortizationInterestHandlingType == null) {
|
||||
reAmortizationInterestHandlingType = LoanReAmortizationInterestHandlingType.DEFAULT;
|
||||
}
|
||||
|
||||
CodeValue reasonCodeValue = null;
|
||||
if (command.parameterExists(LoanReAmortizationApiConstants.reasonCodeValueIdParamName)) {
|
||||
reasonCodeValue = codeValueRepository.findByCodeNameAndId(LoanApiConstants.REAMORTIZATION_REASONS,
|
||||
command.longValueOfParameterNamed(LoanReAmortizationApiConstants.reasonCodeValueIdParamName));
|
||||
}
|
||||
|
||||
return new LoanReAmortizationParameter(reAmortizationTransaction, reAmortizationInterestHandlingType, reasonCodeValue);
|
||||
}
|
||||
}
|
||||
|
||||
+25
-6
@@ -24,23 +24,31 @@ import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ChangeOperation;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LoanReAmortizationValidator {
|
||||
|
||||
private final CodeValueRepository codeValueRepository;
|
||||
|
||||
public void validateReAmortize(Loan loan, JsonCommand command) {
|
||||
validateReAmortizeRequest(command);
|
||||
validateReAmortizeBusinessRules(loan);
|
||||
@@ -54,6 +62,23 @@ public class LoanReAmortizationValidator {
|
||||
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.externalIdParameterName).ignoreIfNull().value(externalId)
|
||||
.notExceedingLengthOf(100);
|
||||
|
||||
final LoanReAmortizationInterestHandlingType reAmortizationInterestHandlingType = command.enumValueOfParameterNamed(
|
||||
LoanReAmortizationApiConstants.reAmortizationInterestHandlingParamName, LoanReAmortizationInterestHandlingType.class);
|
||||
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.reAmortizationInterestHandlingParamName)
|
||||
.value(reAmortizationInterestHandlingType).ignoreIfNull();
|
||||
|
||||
Long reasonCodeValueId = command.longValueOfParameterNamed(LoanReAmortizationApiConstants.reasonCodeValueIdParamName);
|
||||
baseDataValidator.reset().parameter(LoanReAmortizationApiConstants.reasonCodeValueIdParamName).value(reasonCodeValueId)
|
||||
.ignoreIfNull();
|
||||
if (reasonCodeValueId != null) {
|
||||
final CodeValue reasonCodeValue = codeValueRepository.findByCodeNameAndId(LoanApiConstants.REAMORTIZATION_REASONS,
|
||||
reasonCodeValueId);
|
||||
if (reasonCodeValue == null) {
|
||||
dataValidationErrors.add(ApiParameterError.parameterError("validation.msg.reamortization.reason.invalid",
|
||||
"Reamortization Reason with ID " + reasonCodeValueId + " does not exist", LoanApiConstants.REAMORTIZATION_REASONS));
|
||||
}
|
||||
}
|
||||
|
||||
throwExceptionIfValidationErrorsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -78,12 +103,6 @@ public class LoanReAmortizationValidator {
|
||||
loan.getId());
|
||||
}
|
||||
|
||||
// validate reamortization is only available for non-interest bearing loans
|
||||
if (loan.isInterestBearing()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.supported.only.for.non.interest.loans",
|
||||
"Loan reamortization is only available for non-interest bearing loans", loan.getId());
|
||||
}
|
||||
|
||||
// validate reamortization is only done on an active loan
|
||||
if (!loan.getStatus().isActive()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.supported.only.for.active.loans",
|
||||
|
||||
+1
@@ -207,6 +207,7 @@
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationParameter</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory</class>
|
||||
<class>org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest</class>
|
||||
<class>org.apache.fineract.portfolio.loanproduct.domain.LoanProduct</class>
|
||||
|
||||
+9
-15
@@ -53,13 +53,21 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
@SuppressFBWarnings({ "VA_FORMAT_STRING_USES_NEWLINE" })
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class LoanReAmortizationValidatorTest {
|
||||
|
||||
private final LocalDate actualDate = LocalDate.now(Clock.systemUTC());
|
||||
|
||||
private LoanReAmortizationValidator underTest = new LoanReAmortizationValidator();
|
||||
@InjectMocks
|
||||
private LoanReAmortizationValidator underTest;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
@@ -140,20 +148,6 @@ class LoanReAmortizationValidatorTest {
|
||||
.isEqualTo("error.msg.loan.reamortize.supported.only.for.progressive.loan.schedule.type");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateReAmortize_ShouldThrowException_WhenLoanIsInterestBearing() {
|
||||
// given
|
||||
Loan loan = loan();
|
||||
given(loan.isInterestBearing()).willReturn(true);
|
||||
JsonCommand command = jsonCommand();
|
||||
// when
|
||||
GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class,
|
||||
() -> underTest.validateReAmortize(loan, command));
|
||||
// then
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getGlobalisationMessageCode()).isEqualTo("error.msg.loan.reamortize.supported.only.for.non.interest.loans");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateReAmortize_ShouldThrowException_WhenLoanIsNotActive() {
|
||||
// given
|
||||
|
||||
Reference in New Issue
Block a user