FINERACT-2279: Contract Termination - Schedule Handing & Reverse-replay
This commit is contained in:
+36
-5
@@ -345,12 +345,43 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
case REAGE -> handleReAge(loanTransaction, ctx);
|
||||
case ACCRUAL_ACTIVITY -> calculateAccrualActivity(loanTransaction, ctx);
|
||||
case CAPITALIZED_INCOME -> handleCapitalizedIncome(loanTransaction, ctx);
|
||||
case CONTRACT_TERMINATION -> handleContractTermination(loanTransaction, ctx);
|
||||
// TODO: Cover rest of the transaction types
|
||||
default -> log.warn("Unhandled transaction processing for transaction type: {}", loanTransaction.getTypeOf());
|
||||
}
|
||||
return ctx.getChangedTransactionDetail();
|
||||
}
|
||||
|
||||
private void handleContractTermination(final LoanTransaction loanTransaction, final TransactionCtx transactionCtx) {
|
||||
handleAccelerateMaturityDate(loanTransaction, transactionCtx);
|
||||
|
||||
final BigDecimal newInterest = getInterestTillChargeOffForPeriod(loanTransaction.getLoan(), loanTransaction.getTransactionDate(),
|
||||
transactionCtx);
|
||||
createMissingAccrualTransactionDuringChargeOffIfNeeded(newInterest, loanTransaction, loanTransaction.getTransactionDate(),
|
||||
transactionCtx);
|
||||
|
||||
loanTransaction.resetDerivedComponents();
|
||||
// determine how much is outstanding total and breakdown for principal, interest and charges
|
||||
Money principalPortion = Money.zero(transactionCtx.getCurrency());
|
||||
Money interestPortion = Money.zero(transactionCtx.getCurrency());
|
||||
Money feeChargesPortion = Money.zero(transactionCtx.getCurrency());
|
||||
Money penaltyChargesPortion = Money.zero(transactionCtx.getCurrency());
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : transactionCtx.getInstallments()) {
|
||||
principalPortion = principalPortion.plus(currentInstallment.getPrincipalOutstanding(transactionCtx.getCurrency()));
|
||||
interestPortion = interestPortion.plus(currentInstallment.getInterestOutstanding(transactionCtx.getCurrency()));
|
||||
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(transactionCtx.getCurrency()));
|
||||
penaltyChargesPortion = penaltyChargesPortion
|
||||
.plus(currentInstallment.getPenaltyChargesOutstanding(transactionCtx.getCurrency()));
|
||||
}
|
||||
|
||||
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
|
||||
if (isAllComponentsZero(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion)
|
||||
&& loanTransaction.isNotReversed()) {
|
||||
loanTransaction.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleInterestRefund(final LoanTransaction loanTransaction, final TransactionCtx ctx) {
|
||||
final Loan loan = loanTransaction.getLoan();
|
||||
final LoanTransaction chargeOffTransaction = loan.getLoanTransactions().stream().filter(t -> t.isChargeOff() && t.isNotReversed())
|
||||
@@ -1547,7 +1578,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
handleZeroInterestChargeOff(loanTransaction, transactionCtx);
|
||||
} else if (LoanChargeOffBehaviour.ACCELERATE_MATURITY
|
||||
.equals(loanTransaction.getLoan().getLoanProductRelatedDetail().getChargeOffBehaviour())) {
|
||||
handleAccelerateMaturityChargeOff(loanTransaction, transactionCtx);
|
||||
handleAccelerateMaturityDate(loanTransaction, transactionCtx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1590,7 +1621,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
return Arrays.stream(components).allMatch(Money::isZero);
|
||||
}
|
||||
|
||||
private void handleAccelerateMaturityChargeOff(final LoanTransaction loanTransaction, final TransactionCtx transactionCtx) {
|
||||
private void handleAccelerateMaturityDate(final LoanTransaction loanTransaction, final TransactionCtx transactionCtx) {
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final List<LoanRepaymentScheduleInstallment> installments = transactionCtx.getInstallments();
|
||||
final Loan loan = loanTransaction.getLoan();
|
||||
@@ -1665,7 +1696,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
loanSchedule.updateLoanSchedule(loan, installmentsUpToTransactionDate);
|
||||
|
||||
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx && loan.isInterestRecalculationEnabled()) {
|
||||
updateRepaymentPeriodsAfterChargeOff(progressiveTransactionCtx, transactionDate, transactionsToBeReprocessed);
|
||||
updateRepaymentPeriodsAfterAccelerateMaturityDate(progressiveTransactionCtx, transactionDate, transactionsToBeReprocessed);
|
||||
} else {
|
||||
for (LoanTransaction processTransaction : transactionsToBeReprocessed) {
|
||||
final LoanTransaction newTransaction = LoanTransaction.copyTransactionProperties(processTransaction);
|
||||
@@ -2618,8 +2649,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
return loan.getPaymentAllocationRules().stream().filter(e -> e.getTransactionType().isDefault()).findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
private void updateRepaymentPeriodsAfterChargeOff(final ProgressiveTransactionCtx transactionCtx, final LocalDate chargeOffDate,
|
||||
final List<LoanTransaction> transactionsToBeReprocessed) {
|
||||
private void updateRepaymentPeriodsAfterAccelerateMaturityDate(final ProgressiveTransactionCtx transactionCtx,
|
||||
final LocalDate chargeOffDate, final List<LoanTransaction> transactionsToBeReprocessed) {
|
||||
final List<RepaymentPeriod> repaymentPeriods = transactionCtx.getModel().repaymentPeriods();
|
||||
|
||||
if (repaymentPeriods.isEmpty()) {
|
||||
|
||||
+1
@@ -24,4 +24,5 @@
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<!-- Sequence is starting from 5000 to make it easier to move existing liquibase changesets here -->
|
||||
<include file="parts/5001_create_progressive_loan_model.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/5002_add_contract_termination_transaction.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?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_external_event_configuration">
|
||||
<column name="type" value="LoanTransactionContractTerminationPostBusinessEvent"/>
|
||||
<column name="enabled" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="r_enum_value">
|
||||
<column name="enum_name" value="transaction_type_enum"/>
|
||||
<column name="enum_id" valueNumeric="38"/>
|
||||
<column name="enum_message_property" value="Contract Termination"/>
|
||||
<column name="enum_value" value="Contract Termination"/>
|
||||
<column name="enum_type" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="CONTRACT_TERMINATION_LOAN"/>
|
||||
<column name="entity_name" value="LOAN"/>
|
||||
<column name="action_name" value="CONTRACT_TERMINATION"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user