FINERACT-2354: Merge re-aged installment with the next (N+1) installment if they overlap
This commit is contained in:
committed by
Adam Saghy
parent
054e4aee52
commit
5df6af6a23
@@ -1652,3 +1652,123 @@ Feature: LoanReAging
|
|||||||
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
|
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
|
||||||
| NSF fee | true | Specified due date | 01 May 2025 | Flat | 20.0 | 0.0 | 0.0 | 20.0 |
|
| NSF fee | true | Specified due date | 01 May 2025 | Flat | 20.0 | 0.0 | 0.0 | 20.0 |
|
||||||
|
|
||||||
|
Scenario: Verify merging re-aging transaction with N+1 installment in the same bucket(YEARS)
|
||||||
|
When Admin sets the business date to "01 January 2024"
|
||||||
|
When Admin creates a client with random data
|
||||||
|
When Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 100 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
|
||||||
|
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
|
||||||
|
When Admin sets the business date to "05 April 2024"
|
||||||
|
And Admin adds "LOAN_SNOOZE_FEE" due date charge with "03 May 2024" due date and 100 EUR transaction amount
|
||||||
|
When Admin creates a Loan re-aging transaction with the following data:
|
||||||
|
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||||
|
| 1 | YEARS | 01 April 2024 | 3 |
|
||||||
|
Then Loan Repayment schedule has 7 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 01 January 2024 | 01 January 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 01 February 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 3 | 29 | 01 March 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 01 April 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 5 | 0 | 01 April 2024 | | 50.0 | 25.0 | 0.0 | 100.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
|
||||||
|
| 6 | 365 | 01 April 2025 | | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 7 | 365 | 01 April 2026 | | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
|
||||||
|
Scenario: Verify merging re-aging transaction with N+1 installment in the same bucket(MONTHS)
|
||||||
|
When Admin sets the business date to "01 January 2024"
|
||||||
|
When Admin creates a client with random data
|
||||||
|
When Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 100 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
|
||||||
|
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
|
||||||
|
When Admin sets the business date to "05 April 2024"
|
||||||
|
And Admin adds "LOAN_SNOOZE_FEE" due date charge with "03 May 2024" due date and 100 EUR transaction amount
|
||||||
|
When Admin creates a Loan re-aging transaction with the following data:
|
||||||
|
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||||
|
| 1 | MONTHS | 01 April 2024 | 3 |
|
||||||
|
Then Loan Repayment schedule has 7 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 01 January 2024 | 01 January 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 01 February 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 3 | 29 | 01 March 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 01 April 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 5 | 0 | 01 April 2024 | | 50.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 6 | 30 | 01 May 2024 | | 25.0 | 25.0 | 0.0 | 100.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
|
||||||
|
| 7 | 31 | 01 June 2024 | | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
|
||||||
|
Scenario: Verify merging re-aging transaction with N+1 installment in the same bucket(WEEKS)
|
||||||
|
When Admin sets the business date to "01 January 2024"
|
||||||
|
When Admin creates a client with random data
|
||||||
|
When Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 100 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
|
||||||
|
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
|
||||||
|
When Admin sets the business date to "05 April 2024"
|
||||||
|
And Admin adds "LOAN_SNOOZE_FEE" due date charge with "09 April 2024" due date and 100 EUR transaction amount
|
||||||
|
When Admin creates a Loan re-aging transaction with the following data:
|
||||||
|
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||||
|
| 1 | WEEKS | 01 April 2024 | 3 |
|
||||||
|
Then Loan Repayment schedule has 7 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 01 January 2024 | 01 January 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 01 February 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 3 | 29 | 01 March 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 01 April 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 5 | 0 | 01 April 2024 | | 50.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 6 | 7 | 08 April 2024 | | 25.0 | 25.0 | 0.0 | 100.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
|
||||||
|
| 7 | 7 | 15 April 2024 | | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
|
||||||
|
Scenario: Verify merging re-aging transaction with N+1 installment in the same bucket(DAYS)
|
||||||
|
When Admin sets the business date to "01 January 2024"
|
||||||
|
When Admin creates a client with random data
|
||||||
|
When Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 100 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
|
||||||
|
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
|
||||||
|
When Admin sets the business date to "05 April 2024"
|
||||||
|
And Admin adds "LOAN_SNOOZE_FEE" due date charge with "03 April 2024" due date and 100 EUR transaction amount
|
||||||
|
When Admin creates a Loan re-aging transaction with the following data:
|
||||||
|
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||||
|
| 1 | DAYS | 01 April 2024 | 3 |
|
||||||
|
Then Loan Repayment schedule has 7 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 01 January 2024 | 01 January 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 01 February 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 3 | 29 | 01 March 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 01 April 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 5 | 0 | 01 April 2024 | | 50.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 6 | 1 | 02 April 2024 | | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 7 | 1 | 03 April 2024 | | 0.0 | 25.0 | 0.0 | 100.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
|
||||||
|
|
||||||
|
Scenario: Verify re-aging transaction with N+1 installment outside bucket
|
||||||
|
When Admin sets the business date to "01 January 2024"
|
||||||
|
When Admin creates a client with random data
|
||||||
|
When Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 100 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
|
||||||
|
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
|
||||||
|
When Admin sets the business date to "05 April 2024"
|
||||||
|
And Admin adds "LOAN_SNOOZE_FEE" due date charge with "03 July 2024" due date and 100 EUR transaction amount
|
||||||
|
When Admin creates a Loan re-aging transaction with the following data:
|
||||||
|
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||||
|
| 1 | MONTHS | 01 April 2024 | 3 |
|
||||||
|
Then Loan Repayment schedule has 8 periods, with the following data for periods:
|
||||||
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 01 January 2024 | 01 January 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 01 February 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 3 | 29 | 01 March 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 01 April 2024 | 05 April 2024 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 5 | 0 | 01 April 2024 | | 50.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 6 | 30 | 01 May 2024 | | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 7 | 31 | 01 June 2024 | | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 |
|
||||||
|
| 8 | 32 | 03 July 2024 | | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 |
|
||||||
+75
-23
@@ -68,6 +68,7 @@ import org.apache.fineract.infrastructure.core.service.MathUtil;
|
|||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||||
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData;
|
import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData;
|
||||||
@@ -2847,12 +2848,18 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
MonetaryCurrency currency = ctx.getCurrency();
|
MonetaryCurrency currency = ctx.getCurrency();
|
||||||
List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
|
List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
|
||||||
|
|
||||||
AtomicReference<Money> outstandingPrincipalBalance = new AtomicReference<>(Money.zero(currency));
|
final LocalDate startDate = loanTransaction.getLoanReAgeParameter().getStartDate();
|
||||||
|
final LocalDate endDate = calculateReAgedInstallmentEndDate(loanTransaction.getLoanReAgeParameter());
|
||||||
|
final AtomicReference<Money> outstandingPrincipalBalance = new AtomicReference<>(Money.zero(currency));
|
||||||
installments.forEach(i -> {
|
installments.forEach(i -> {
|
||||||
Money principalOutstanding = i.getPrincipalOutstanding(currency);
|
final boolean shouldInclude = !i.isAdditional() || i.getDueDate().isBefore(startDate)
|
||||||
if (principalOutstanding.isGreaterThanZero()) {
|
|| (!i.getDueDate().isBefore(startDate) && i.getDueDate().isBefore(endDate));
|
||||||
outstandingPrincipalBalance.set(outstandingPrincipalBalance.get().add(principalOutstanding));
|
if (shouldInclude) {
|
||||||
i.addToPrincipal(loanTransaction.getTransactionDate(), principalOutstanding.negated());
|
Money principalOutstanding = i.getPrincipalOutstanding(currency);
|
||||||
|
if (principalOutstanding.isGreaterThanZero()) {
|
||||||
|
outstandingPrincipalBalance.set(outstandingPrincipalBalance.get().add(principalOutstanding));
|
||||||
|
i.addToPrincipal(loanTransaction.getTransactionDate(), principalOutstanding.negated());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2873,25 +2880,26 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
.minus(calculatedPrincipal.multipliedBy(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments()));
|
.minus(calculatedPrincipal.multipliedBy(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments()));
|
||||||
}
|
}
|
||||||
final LoanRepaymentScheduleInstallment lastNormalInstallment = installments.stream() //
|
final LoanRepaymentScheduleInstallment lastNormalInstallment = installments.stream() //
|
||||||
.filter(i -> i.getDueDate().isBefore(loanTransaction.getTransactionDate())) //
|
.filter(i -> !i.isDownPayment() && i.getDueDate().isBefore(loanTransaction.getTransactionDate())) //
|
||||||
.reduce((first, second) -> second) //
|
.reduce((first, second) -> second) //
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
LoanRepaymentScheduleInstallment reAgedInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(
|
|
||||||
lastNormalInstallment.getLoan(), lastNormalInstallment.getInstallmentNumber() + 1, lastNormalInstallment.getDueDate(),
|
LoanRepaymentScheduleInstallment reAgedInstallment = createOrConvertReAgedInstallment(installments, startDate,
|
||||||
loanTransaction.getLoanReAgeParameter().getStartDate(), calculatedPrincipal.getAmount());
|
loanTransaction.getLoanReAgeParameter(), lastNormalInstallment.getLoan(), lastNormalInstallment.getInstallmentNumber() + 1,
|
||||||
installments.add(reAgedInstallment);
|
lastNormalInstallment.getDueDate(), calculatedPrincipal.getAmount());
|
||||||
reAgedInstallment.updateObligationsMet(currency, loanTransaction.getTransactionDate());
|
reAgedInstallment.updateObligationsMet(currency, loanTransaction.getTransactionDate());
|
||||||
|
|
||||||
for (int i = 1; i < loanTransaction.getLoanReAgeParameter().getNumberOfInstallments(); i++) {
|
for (int i = 1; i < loanTransaction.getLoanReAgeParameter().getNumberOfInstallments(); i++) {
|
||||||
LocalDate calculatedDueDate = calculateReAgedInstallmentDueDate(loanTransaction.getLoanReAgeParameter(),
|
final LocalDate calculatedDueDate = calculateReAgedInstallmentDueDate(loanTransaction.getLoanReAgeParameter(),
|
||||||
reAgedInstallment.getDueDate());
|
reAgedInstallment.getDueDate());
|
||||||
reAgedInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(reAgedInstallment.getLoan(),
|
|
||||||
reAgedInstallment.getInstallmentNumber() + 1, reAgedInstallment.getDueDate(), calculatedDueDate,
|
reAgedInstallment = createOrConvertReAgedInstallment(installments, calculatedDueDate, loanTransaction.getLoanReAgeParameter(),
|
||||||
|
reAgedInstallment.getLoan(), reAgedInstallment.getInstallmentNumber() + 1, reAgedInstallment.getDueDate(),
|
||||||
calculatedPrincipal.getAmount());
|
calculatedPrincipal.getAmount());
|
||||||
installments.add(reAgedInstallment);
|
|
||||||
reAgedInstallment.updateObligationsMet(currency, loanTransaction.getTransactionDate());
|
reAgedInstallment.updateObligationsMet(currency, loanTransaction.getTransactionDate());
|
||||||
}
|
}
|
||||||
reAgedInstallment.addToPrincipal(loanTransaction.getTransactionDate(), adjustCalculatedPrincipal);
|
reAgedInstallment.addToPrincipal(loanTransaction.getTransactionDate(), adjustCalculatedPrincipal);
|
||||||
|
|
||||||
reprocessInstallments(installments);
|
reprocessInstallments(installments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2905,21 +2913,65 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
installments.stream().sorted(LoanRepaymentScheduleInstallment::compareToByDueDate).forEachOrdered(i -> {
|
installments.stream().sorted(LoanRepaymentScheduleInstallment::compareToByDueDate).forEachOrdered(i -> {
|
||||||
i.updateInstallmentNumber(counter.getAndIncrement());
|
i.updateInstallmentNumber(counter.getAndIncrement());
|
||||||
final LocalDate prev = previousDueDate.get();
|
final LocalDate prev = previousDueDate.get();
|
||||||
|
if (prev != null && (i.isAdditional() || i.isReAged())) {
|
||||||
if (prev != null && i.isAdditional()) {
|
|
||||||
i.updateFromDate(prev);
|
i.updateFromDate(prev);
|
||||||
}
|
}
|
||||||
previousDueDate.set(i.getDueDate());
|
previousDueDate.set(i.getDueDate());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate calculateReAgedInstallmentDueDate(LoanReAgeParameter reAgeParameter, LocalDate dueDate) {
|
private LoanRepaymentScheduleInstallment convertAdditionalToReAged(final LoanRepaymentScheduleInstallment installment,
|
||||||
return switch (reAgeParameter.getFrequencyType()) {
|
final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principalAmount) {
|
||||||
case DAYS -> dueDate.plusDays(reAgeParameter.getFrequencyNumber());
|
installment.setAdditional(false);
|
||||||
case WEEKS -> dueDate.plusWeeks(reAgeParameter.getFrequencyNumber());
|
installment.setReAged(true);
|
||||||
case MONTHS -> dueDate.plusMonths(reAgeParameter.getFrequencyNumber());
|
installment.updateFromDate(fromDate);
|
||||||
case YEARS -> dueDate.plusYears(reAgeParameter.getFrequencyNumber());
|
installment.updateDueDate(dueDate);
|
||||||
default -> throw new UnsupportedOperationException(reAgeParameter.getFrequencyType().getCode());
|
installment.updatePrincipal(principalAmount);
|
||||||
|
return installment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<LoanRepaymentScheduleInstallment> findAdditionalForFrequency(final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
|
final LocalDate targetDueDate, final LoanReAgeParameter reAgeParameter) {
|
||||||
|
final LocalDate nextReAgedDate = calculateReAgedInstallmentDueDate(reAgeParameter, targetDueDate);
|
||||||
|
|
||||||
|
return installments.stream() //
|
||||||
|
.filter(LoanRepaymentScheduleInstallment::isAdditional) //
|
||||||
|
.filter(ai -> !ai.getDueDate().isBefore(targetDueDate) && ai.getDueDate().isBefore(nextReAgedDate)).findAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanRepaymentScheduleInstallment createOrConvertReAgedInstallment(final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
|
final LocalDate targetDueDate, final LoanReAgeParameter reAgeParameter, final Loan loan, final int installmentNumber,
|
||||||
|
final LocalDate fromDate, final BigDecimal principalAmount) {
|
||||||
|
final Optional<LoanRepaymentScheduleInstallment> additionalInstallment = findAdditionalForFrequency(installments, targetDueDate,
|
||||||
|
reAgeParameter);
|
||||||
|
|
||||||
|
if (additionalInstallment.isPresent()) {
|
||||||
|
return convertAdditionalToReAged(additionalInstallment.get(), fromDate, targetDueDate, principalAmount);
|
||||||
|
} else {
|
||||||
|
final LoanRepaymentScheduleInstallment reAgedInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(loan,
|
||||||
|
installmentNumber, fromDate, targetDueDate, principalAmount);
|
||||||
|
installments.add(reAgedInstallment);
|
||||||
|
return reAgedInstallment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDate calculateReAgedInstallmentEndDate(final LoanReAgeParameter reAgeParameter) {
|
||||||
|
return calculateReAgedNextDate(reAgeParameter.getFrequencyType(), reAgeParameter.getStartDate(),
|
||||||
|
reAgeParameter.getFrequencyNumber(), reAgeParameter.getNumberOfInstallments());
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDate calculateReAgedInstallmentDueDate(final LoanReAgeParameter reAgeParameter, final LocalDate dueDate) {
|
||||||
|
return calculateReAgedNextDate(reAgeParameter.getFrequencyType(), dueDate, reAgeParameter.getFrequencyNumber(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDate calculateReAgedNextDate(final PeriodFrequencyType frequencyType, final LocalDate dueDate,
|
||||||
|
final Integer frequencyNumber, final Integer numberOfInstallments) {
|
||||||
|
return switch (frequencyType) {
|
||||||
|
case DAYS -> dueDate.plusDays((long) frequencyNumber * numberOfInstallments);
|
||||||
|
case WEEKS -> dueDate.plusWeeks((long) frequencyNumber * numberOfInstallments);
|
||||||
|
case MONTHS -> dueDate.plusMonths((long) frequencyNumber * numberOfInstallments);
|
||||||
|
case YEARS -> dueDate.plusYears((long) frequencyNumber * numberOfInstallments);
|
||||||
|
default -> throw new UnsupportedOperationException();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user