FINERACT-2354: Third step - backdated re-age edge case for Interest bearing loans - Default Behavior, interestRecalculation = true, without dueDate change
This commit is contained in:
committed by
Adam Saghy
parent
01ca515e8f
commit
5a1c6edbef
+4
-3
@@ -233,8 +233,9 @@ public class LoanReAgingStepDef extends AbstractStepDef {
|
||||
eventAssertion.assertEventRaised(LoanReAgeEvent.class, loanId);
|
||||
}
|
||||
|
||||
@When("Admin fails to create a Loan re-aging transaction with error {string} and with the following data:")
|
||||
public void adminFailsToCreateReAgingTransactionWithError(final String expectedError, final DataTable table) throws IOException {
|
||||
@When("Admin fails to create a Loan re-aging transaction with status code {int} error {string} and with the following data:")
|
||||
public void adminFailsToCreateReAgingTransactionWithError(final int statusCode, final String expectedError, final DataTable table)
|
||||
throws IOException {
|
||||
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
final long loanId = loanResponse.body().getLoanId();
|
||||
|
||||
@@ -259,7 +260,7 @@ public class LoanReAgingStepDef extends AbstractStepDef {
|
||||
assertThat(errorBody.string()).contains(expectedError);
|
||||
}
|
||||
|
||||
ErrorHelper.checkFailedApiCall(response, 403);
|
||||
ErrorHelper.checkFailedApiCall(response, statusCode);
|
||||
}
|
||||
|
||||
@Then("Admin fails to create a Loan re-aging transaction with the following data because loan was charged-off:")
|
||||
|
||||
@@ -3473,7 +3473,7 @@ Feature: LoanReAging
|
||||
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
|
||||
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
|
||||
| 20 February 2024 | Repayment | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
|
||||
When Admin fails to create a Loan re-aging transaction with error "error.msg.loan.reage.no.outstanding.balance.to.reage" and with the following data:
|
||||
When Admin fails to create a Loan re-aging transaction with status code 403 error "error.msg.loan.reage.no.outstanding.balance.to.reage" and with the following data:
|
||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
| 1 | MONTHS | 01 March 2024 | 3 |
|
||||
Then Loan Repayment schedule has 5 periods, with the following data for periods:
|
||||
@@ -5354,6 +5354,242 @@ Feature: LoanReAging
|
||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | true | true |
|
||||
| 16 March 2024 | Repayment | 14.3 | 13.81 | 0.49 | 0.0 | 0.0 | 69.76 | false | true |
|
||||
|
||||
When Loan Pay-off is made on "01 April 2024"
|
||||
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
||||
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
||||
|
||||
@TestRailId:C4110 @AdvancedPaymentAllocation
|
||||
Scenario: Verify allowing Re-aging on interest bearing loan - Interest calculation: Default Behavior - backdated re-aging transaction - UC16
|
||||
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_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 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
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
When Admin sets the business date to "01 February 2024"
|
||||
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
||||
When Admin sets the business date to "01 March 2024"
|
||||
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 17.01 EUR transaction amount
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 29 | 01 March 2024 | 01 March 2024 | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 34.02 | 0.0 | 0.0 | 68.03 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
||||
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false |
|
||||
When Admin sets the business date to "01 June 2024"
|
||||
# Backdated re-aging - created in June but effective from April 01
|
||||
When Admin creates a Loan re-aging transaction with the following data:
|
||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
| 1 | MONTHS | 01 April 2024 | 6 |
|
||||
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 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 29 | 01 March 2024 | 01 March 2024 | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 01 April 2024 | | 56.0 | 11.05 | 0.39 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
||||
| 4 | 30 | 01 May 2024 | | 44.95 | 11.05 | 0.39 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
||||
| 5 | 31 | 01 June 2024 | | 33.9 | 11.05 | 0.39 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
||||
| 6 | 30 | 01 July 2024 | | 22.66 | 11.24 | 0.2 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
||||
| 7 | 31 | 01 August 2024 | | 11.35 | 11.31 | 0.13 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
||||
| 8 | 31 | 01 September 2024| | 0.0 | 11.35 | 0.07 | 0.0 | 0.0 | 11.42 | 0.0 | 0.0 | 0.0 | 11.42 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.64 | 0.0 | 0.0 | 102.64 | 34.02 | 0.0 | 0.0 | 68.62 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false | false |
|
||||
| 01 April 2024 | Re-age | 67.44 | 67.05 | 0.39 | 0.0 | 0.0 | 0.0 | false | true |
|
||||
|
||||
@TestRailId:C4154 @AdvancedPaymentAllocation
|
||||
Scenario: Verify allowing Re-aging on interest bearing loan - Interest calculation: Default Behavior - re-aging on same day as disbursement - UC16.1
|
||||
When Admin sets the business date to "01 January 2024"
|
||||
When Admin creates a client with random data
|
||||
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
|
||||
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_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 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
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
When Admin creates a Loan re-aging transaction with the following data:
|
||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
| 1 | MONTHS | 01 January 2024 | 6 |
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
| 01 January 2024 | Re-age | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
|
||||
|
||||
@TestRailId:C4154 @AdvancedPaymentAllocation
|
||||
Scenario: Verify allowing Re-aging on interest bearing loan - Interest calculation: Default Behavior - validation that reAgeStartDate must be after disbursement date - UC16.2
|
||||
When Admin sets the business date to "01 January 2024"
|
||||
When Admin creates a client with random data
|
||||
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
|
||||
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_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 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
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
When Admin sets the business date to "15 February 2024"
|
||||
When Admin fails to create a Loan re-aging transaction with status code 400 error "validation.msg.validation.errors.exist" and with the following data:
|
||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
| 1 | MONTHS | 31 December 2023 | 6 |
|
||||
|
||||
@TestRailId:C4156 @AdvancedPaymentAllocation
|
||||
Scenario: Verify allowing Re-aging on interest bearing loan - Interest calculation: Default Behavior - Verify backdated re-aging on the day of repayment with interest recalculation enabled - UC16.3
|
||||
When Admin sets the business date to "01 January 2024"
|
||||
When Admin creates a client with random data
|
||||
When Admin set "LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ZERO_INTEREST_CHARGE_OFF" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
|
||||
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_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ZERO_INTEREST_CHARGE_OFF | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 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
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
When Admin sets the business date to "01 February 2024"
|
||||
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 17.01 | 0.0 | 0.0 | 85.04 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
||||
When Admin sets the business date to "01 March 2024"
|
||||
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 17.01 EUR transaction amount
|
||||
Then Loan Repayment schedule has 6 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 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 29 | 01 March 2024 | 01 March 2024 | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
|
||||
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 34.02 | 0.0 | 0.0 | 68.03 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
||||
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false |
|
||||
When Admin sets the business date to "01 June 2024"
|
||||
When Admin creates a Loan re-aging transaction with the following data:
|
||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
| 1 | MONTHS | 01 February 2024 | 6 |
|
||||
#Internal Server Error 500 is seen here, needs fixing
|
||||
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 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 29 | 01 March 2024 | 01 March 2024 | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 01 April 2024 | | 56.02 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 |
|
||||
| 4 | 30 | 01 May 2024 | | 44.99 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 |
|
||||
| 5 | 31 | 01 June 2024 | | 33.96 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 |
|
||||
| 6 | 30 | 01 July 2024 | | 22.93 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 | 0.0 | 0.0 | 0.0 | 11.03 |
|
||||
| 7 | 31 | 01 August 2024 | | 0.0 | 22.93 | 0.0 | 0.0 | 0.0 | 22.93 | 0.0 | 0.0 | 0.0 | 22.93 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 100.0 | 1.07 | 0.0 | 0.0 | 101.07 | 34.02 | 0.0 | 0.0 | 67.05 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false | false |
|
||||
| 01 March 2024 | Re-age | 67.05 | 67.05 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||
|
||||
+17
-117
@@ -1578,49 +1578,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
super.handleWriteOff(transaction, ctx.getCurrency(), ctx.getInstallments());
|
||||
}
|
||||
|
||||
private List<RepaymentPeriod> findPossiblyOverdueRepaymentPeriods(LocalDate targetDate, ProgressiveLoanInterestScheduleModel model) {
|
||||
return model.repaymentPeriods().stream() //
|
||||
.filter(repaymentPeriod -> DateUtils.isAfter(targetDate, repaymentPeriod.getDueDate())).toList();
|
||||
}
|
||||
|
||||
public boolean rework(LocalDate targetDate, ProgressiveTransactionCtx ctx) {
|
||||
boolean hasChange = false;
|
||||
ProgressiveLoanInterestScheduleModel model = ctx.getModel();
|
||||
List<RepaymentPeriod> overdueInstallmentsSortedByInstallmentNumber = findPossiblyOverdueRepaymentPeriods(targetDate, model);
|
||||
if (!overdueInstallmentsSortedByInstallmentNumber.isEmpty()) {
|
||||
RepaymentPeriod lastPeriod = model.getLastRepaymentPeriod();
|
||||
RepaymentPeriod currentPeriod = model.findRepaymentPeriod(targetDate).orElse(lastPeriod);
|
||||
MonetaryCurrency currency = model.zero().getCurrency();
|
||||
Money overDuePrincipal = Money.zero(currency);
|
||||
Money aggregatedOverDuePrincipal = Money.zero(currency);
|
||||
for (RepaymentPeriod processingPeriod : overdueInstallmentsSortedByInstallmentNumber) {
|
||||
// add and subtract outstanding principal
|
||||
if (!overDuePrincipal.isZero()) {
|
||||
boolean currentChanges = adjustOverduePrincipal(targetDate, processingPeriod, overDuePrincipal,
|
||||
aggregatedOverDuePrincipal, ctx);
|
||||
|
||||
hasChange = hasChange || currentChanges;
|
||||
}
|
||||
|
||||
overDuePrincipal = processingPeriod.getOutstandingPrincipal();
|
||||
aggregatedOverDuePrincipal = aggregatedOverDuePrincipal.add(overDuePrincipal);
|
||||
}
|
||||
|
||||
if (!currentPeriod.equals(lastPeriod) || !targetDate.isAfter(lastPeriod.getDueDate())) {
|
||||
boolean currentChanges = adjustOverduePrincipal(targetDate, currentPeriod, overDuePrincipal, aggregatedOverDuePrincipal,
|
||||
ctx);
|
||||
hasChange = hasChange || currentChanges;
|
||||
|
||||
}
|
||||
if (aggregatedOverDuePrincipal.isGreaterThanZero()
|
||||
&& (model.lastOverdueBalanceChange() == null || model.lastOverdueBalanceChange().isBefore(targetDate))) {
|
||||
model.lastOverdueBalanceChange(targetDate);
|
||||
}
|
||||
}
|
||||
|
||||
return hasChange;
|
||||
}
|
||||
|
||||
public void recalculateInterestForDate(LocalDate targetDate, ProgressiveTransactionCtx ctx) {
|
||||
recalculateInterestForDate(targetDate, ctx, true);
|
||||
}
|
||||
@@ -1631,7 +1588,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
if (isInterestRecalculationSupported(ctx, loan) && !loan.isNpa()
|
||||
&& !loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue()) {
|
||||
|
||||
boolean modelHasUpdates = rework(targetDate, ctx);
|
||||
boolean modelHasUpdates = emiCalculator.recalculateModelOverdueAmountsTillDate(ctx, targetDate);
|
||||
if (modelHasUpdates && updateInstallments) {
|
||||
updateInstallmentsPrincipalAndInterestByModel(ctx);
|
||||
}
|
||||
@@ -1639,38 +1596,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
}
|
||||
|
||||
private boolean adjustOverduePrincipal(LocalDate currentDate, RepaymentPeriod currentInstallment, Money overduePrincipal,
|
||||
Money aggregatedOverDuePrincipal, ProgressiveTransactionCtx ctx) {
|
||||
|
||||
LocalDate fromDate = currentInstallment.getFromDate();
|
||||
LocalDate toDate = currentInstallment.getDueDate();
|
||||
ProgressiveLoanInterestScheduleModel model = ctx.getModel();
|
||||
boolean prepayAttempt = ctx.isPrepayAttempt();
|
||||
LoanInterestRecalculationDetails loanInterestRecalculationDetails = ctx.getInstallments().getFirst().getLoan()
|
||||
.getLoanInterestRecalculationDetails();
|
||||
|
||||
if (!currentDate.equals(model.lastOverdueBalanceChange())) {
|
||||
if (model.lastOverdueBalanceChange() == null || currentInstallment.getFromDate().isAfter(model.lastOverdueBalanceChange())) {
|
||||
emiCalculator.addBalanceCorrection(model, fromDate, overduePrincipal);
|
||||
} else {
|
||||
emiCalculator.addBalanceCorrection(model, model.lastOverdueBalanceChange(), overduePrincipal);
|
||||
}
|
||||
|
||||
if (currentDate.isAfter(fromDate) && !currentDate.isAfter(toDate)) {
|
||||
LocalDate lastOverdueBalanceChange;
|
||||
if (shouldRecalculateTillInstallmentDueDate(loanInterestRecalculationDetails, prepayAttempt)) {
|
||||
lastOverdueBalanceChange = toDate;
|
||||
} else {
|
||||
lastOverdueBalanceChange = currentDate;
|
||||
}
|
||||
emiCalculator.addBalanceCorrection(model, lastOverdueBalanceChange, aggregatedOverDuePrincipal.negated());
|
||||
model.lastOverdueBalanceChange(lastOverdueBalanceChange);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateInstallmentsPrincipalAndInterestByModel(ProgressiveTransactionCtx ctx) {
|
||||
ctx.getModel().repaymentPeriods().forEach(repayment -> {
|
||||
LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
|
||||
@@ -2991,9 +2916,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupInstallmentsAndRepaymentPeriodsAfterReAging(final ProgressiveTransactionCtx ctx) {
|
||||
private void cleanupInstallmentsAfterReAging(final ProgressiveTransactionCtx ctx) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
|
||||
final List<RepaymentPeriod> repaymentPeriods = ctx.getModel().repaymentPeriods();
|
||||
|
||||
// Find the last re-aged installment number
|
||||
final OptionalInt lastReAgedInstallmentNumberOpt = installments.stream().filter(LoanRepaymentScheduleInstallment::isReAged)
|
||||
@@ -3001,17 +2925,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
|
||||
if (lastReAgedInstallmentNumberOpt.isPresent()) {
|
||||
final int lastReAgedInstallmentNumber = lastReAgedInstallmentNumberOpt.getAsInt();
|
||||
final LoanRepaymentScheduleInstallment lastReAgedInstallment = installments.stream()
|
||||
.filter(i -> i.getInstallmentNumber().equals(lastReAgedInstallmentNumber)).findFirst().orElse(null);
|
||||
// Remove installments with numbers greater than the last re-aged installment
|
||||
final List<LoanRepaymentScheduleInstallment> installmentsToRemove = installments.stream().filter(i -> i != null
|
||||
&& !i.isAdditional() && i.getInstallmentNumber() != null && i.getInstallmentNumber() > lastReAgedInstallmentNumber)
|
||||
.toList();
|
||||
if (lastReAgedInstallment != null) {
|
||||
final List<RepaymentPeriod> repaymentPeriodsToRemove = repaymentPeriods.stream()
|
||||
.filter(rp -> !rp.getFromDate().isBefore(lastReAgedInstallment.getDueDate())).toList();
|
||||
repaymentPeriodsToRemove.forEach(repaymentPeriods::remove);
|
||||
}
|
||||
installmentsToRemove.forEach(installments::remove);
|
||||
}
|
||||
}
|
||||
@@ -3050,7 +2967,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
});
|
||||
|
||||
cleanupInstallmentsAndRepaymentPeriodsAfterReAging(ctx);
|
||||
cleanupInstallmentsAfterReAging(ctx);
|
||||
}
|
||||
|
||||
private void reprocessInstallments(final List<LoanRepaymentScheduleInstallment> installments) {
|
||||
@@ -3307,47 +3224,29 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
final MonetaryCurrency currency = ctx.getCurrency();
|
||||
final Loan loan = loanTransaction.getLoan();
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
|
||||
final List<RepaymentPeriod> repaymentPeriods = ctx.getModel().repaymentPeriods();
|
||||
final LocalDate reAgingStartDate = loanTransaction.getLoanReAgeParameter().getStartDate();
|
||||
|
||||
final List<RepaymentPeriod> periodsBeforeReAging = repaymentPeriods.stream()
|
||||
.filter(rp -> rp.getFromDate().isBefore(reAgingStartDate) && !rp.isFullyPaid()).toList();
|
||||
final List<LoanRepaymentScheduleInstallment> installmentsBeforeReAging = installments.stream()
|
||||
.filter(rp -> rp.getFromDate().isBefore(reAgingStartDate)).toList();
|
||||
|
||||
final RepaymentPeriod lastPeriod = periodsBeforeReAging.getLast();
|
||||
// Define the date, which must match reAgingStartDate
|
||||
final LocalDate expectedReAgingDate = installmentsBeforeReAging.isEmpty() ? installments.getFirst().getFromDate()
|
||||
: installmentsBeforeReAging.getLast().getDueDate();
|
||||
|
||||
if (!lastPeriod.getDueDate().isEqual(reAgingStartDate)) {
|
||||
if (!expectedReAgingDate.isEqual(reAgingStartDate)) {
|
||||
// TODO: implement logic when re-aging changes the due dates
|
||||
throw new NotImplementedException("Logic when re-aging changes the due dates not implemented");
|
||||
}
|
||||
|
||||
final BigDecimal interestBeforeReAging = emiCalculator
|
||||
.getPeriodInterestTillDate(ctx.getModel(), lastPeriod.getDueDate(), transactionDate, false).getAmount();
|
||||
|
||||
final AtomicReference<Money> outstandingPrincipalBalance = new AtomicReference<>(Money.zero(currency));
|
||||
installments.forEach(i -> {
|
||||
final Money principalOutstanding = i.getPrincipalOutstanding(currency);
|
||||
if (principalOutstanding.isGreaterThanZero()) {
|
||||
outstandingPrincipalBalance.set(outstandingPrincipalBalance.get().add(principalOutstanding));
|
||||
}
|
||||
});
|
||||
|
||||
final AtomicReference<BigDecimal> interestFromZeroedInstallments = new AtomicReference<>(interestBeforeReAging);
|
||||
|
||||
installments.stream()
|
||||
.filter(installment -> !installment.isObligationsMet() && !installment.getDueDate().isAfter(lastPeriod.getFromDate()))
|
||||
.forEach(installment -> {
|
||||
final BigDecimal currentInterest = interestFromZeroedInstallments.get();
|
||||
final BigDecimal additionalInterest = MathUtil.nullToZero(installment.getInterestOutstanding(currency).getAmount()
|
||||
.add(MathUtil.nullToZero(installment.getCreditedInterest()).negate()));
|
||||
interestFromZeroedInstallments.set(currentInterest.add(additionalInterest));
|
||||
});
|
||||
final Money interestFromZeroedInstallments = emiCalculator.getOutstandingInterestTillDate(ctx.getModel(),
|
||||
loanTransaction.getTransactionDate());
|
||||
|
||||
final BigDecimal interestRate = loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate();
|
||||
final Money totalOutstandingPrincipal = ctx.getModel().getTotalOutstandingPrincipal();
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = new LoanApplicationTerms.Builder().currency(currency.getCurrencyData())
|
||||
.repaymentsStartingFromDate(reAgingStartDate).principal(outstandingPrincipalBalance.get())
|
||||
.repaymentsStartingFromDate(reAgingStartDate).principal(totalOutstandingPrincipal)
|
||||
.loanTermFrequency(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments())
|
||||
.loanTermPeriodFrequencyType(loanTransaction.getLoanReAgeParameter().getFrequencyType())
|
||||
.numberOfRepayments(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments())
|
||||
@@ -3366,11 +3265,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.mc(mc).build();
|
||||
|
||||
// Update the existing model with re-aged periods
|
||||
emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx.getModel(), loanTransaction, loanApplicationTerms, mc);
|
||||
emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx, loanTransaction, loanApplicationTerms, mc);
|
||||
|
||||
updateInstallmentsByModelForReAging(loanTransaction, ctx);
|
||||
|
||||
loanTransaction.updateComponentsAndTotal(outstandingPrincipalBalance.get(),
|
||||
Money.of(currency, interestFromZeroedInstallments.get()), Money.zero(currency), Money.zero(currency));
|
||||
loanTransaction.updateComponentsAndTotal(totalOutstandingPrincipal, interestFromZeroedInstallments, Money.zero(currency),
|
||||
Money.zero(currency));
|
||||
reprocessInstallments(installments);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -28,6 +28,7 @@ import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ProgressiveTransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.data.OutstandingDetails;
|
||||
@@ -143,6 +144,14 @@ public interface EMICalculator {
|
||||
*/
|
||||
void applyInterestPause(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate fromDate, LocalDate endDate);
|
||||
|
||||
void updateModelRepaymentPeriodsDuringReAge(ProgressiveLoanInterestScheduleModel scheduleModel, LoanTransaction loanTransaction,
|
||||
void updateModelRepaymentPeriodsDuringReAge(ProgressiveTransactionCtx ctx, LoanTransaction loanTransaction,
|
||||
LoanApplicationTerms loanApplicationTerms, MathContext mc);
|
||||
|
||||
boolean recalculateModelOverdueAmountsTillDate(ProgressiveTransactionCtx ctx, LocalDate targetDate);
|
||||
|
||||
/**
|
||||
* Gives back the sum of the outstanding interest from the whole model till the provided date.
|
||||
*/
|
||||
@NotNull
|
||||
Money getOutstandingInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate tillDate);
|
||||
}
|
||||
|
||||
+166
-17
@@ -39,6 +39,7 @@ import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
@@ -46,10 +47,13 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ProgressiveTransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
|
||||
@@ -571,20 +575,126 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModelRepaymentPeriodsDuringReAge(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final LoanTransaction loanTransaction, final LoanApplicationTerms loanApplicationTerms, final MathContext mc) {
|
||||
final LoanReAgeParameter loanReAgeParameter = loanTransaction.getLoanReAgeParameter();
|
||||
final LocalDate reAgingStartDate = loanReAgeParameter.getStartDate();
|
||||
final List<RepaymentPeriod> existingRepaymentPeriods = scheduleModel.repaymentPeriods();
|
||||
public void updateModelRepaymentPeriodsDuringReAge(final ProgressiveTransactionCtx ctx, final LoanTransaction loanTransaction,
|
||||
final LoanApplicationTerms loanApplicationTerms, final MathContext mc) {
|
||||
final List<RepaymentPeriod> existingRepaymentPeriods = ctx.getModel().repaymentPeriods();
|
||||
|
||||
moveOutstandingAmountsFromPeriodsBeforeReAging(existingRepaymentPeriods, reAgingStartDate);
|
||||
moveOutstandingAmountsFromPeriodsBeforeReAging(existingRepaymentPeriods, loanTransaction.getLoanReAgeParameter().getStartDate());
|
||||
|
||||
final LocalDate periodStartDate = calculateFirstReAgedPeriodStartDate(loanReAgeParameter);
|
||||
final LocalDate periodStartDate = calculateFirstReAgedPeriodStartDate(loanTransaction);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generateTemporaryReAgedScheduleModel(loanApplicationTerms,
|
||||
mc, periodStartDate);
|
||||
|
||||
mergeNewInterestScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, loanTransaction);
|
||||
mergeNewInterestScheduleModelWithExistingOne(ctx, temporaryReAgedScheduleModel, loanTransaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean recalculateModelOverdueAmountsTillDate(final ProgressiveTransactionCtx ctx, final LocalDate targetDate) {
|
||||
boolean hasChange = false;
|
||||
final ProgressiveLoanInterestScheduleModel model = ctx.getModel();
|
||||
final List<RepaymentPeriod> overdueInstallmentsSortedByInstallmentNumber = findPossiblyOverdueRepaymentPeriods(targetDate, model);
|
||||
if (!overdueInstallmentsSortedByInstallmentNumber.isEmpty()) {
|
||||
final RepaymentPeriod lastPeriod = model.getLastRepaymentPeriod();
|
||||
final RepaymentPeriod currentPeriod = model.findRepaymentPeriod(targetDate).orElse(lastPeriod);
|
||||
final MonetaryCurrency currency = model.zero().getCurrency();
|
||||
Money overDuePrincipal = Money.zero(currency);
|
||||
Money aggregatedOverDuePrincipal = Money.zero(currency);
|
||||
for (RepaymentPeriod processingPeriod : overdueInstallmentsSortedByInstallmentNumber) {
|
||||
// add and subtract outstanding principal
|
||||
if (!overDuePrincipal.isZero()) {
|
||||
final boolean currentChanges = adjustOverduePrincipal(targetDate, processingPeriod, overDuePrincipal,
|
||||
aggregatedOverDuePrincipal, ctx);
|
||||
|
||||
hasChange = hasChange || currentChanges;
|
||||
}
|
||||
|
||||
overDuePrincipal = processingPeriod.getOutstandingPrincipal();
|
||||
aggregatedOverDuePrincipal = aggregatedOverDuePrincipal.add(overDuePrincipal);
|
||||
}
|
||||
|
||||
if (!currentPeriod.equals(lastPeriod) || !targetDate.isAfter(lastPeriod.getDueDate())) {
|
||||
final boolean currentChanges = adjustOverduePrincipal(targetDate, currentPeriod, overDuePrincipal,
|
||||
aggregatedOverDuePrincipal, ctx);
|
||||
hasChange = hasChange || currentChanges;
|
||||
|
||||
}
|
||||
if (aggregatedOverDuePrincipal.isGreaterThanZero()
|
||||
&& (model.lastOverdueBalanceChange() == null || model.lastOverdueBalanceChange().isBefore(targetDate))) {
|
||||
model.lastOverdueBalanceChange(targetDate);
|
||||
}
|
||||
}
|
||||
|
||||
return hasChange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Money getOutstandingInterestTillDate(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate tillDate) {
|
||||
final ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
|
||||
tillDate);
|
||||
return recalculatedScheduleModelTillDate.repaymentPeriods().stream()
|
||||
.filter(repaymentPeriod -> repaymentPeriod.getFromDate().isBefore(tillDate)).map(RepaymentPeriod::getOutstandingInterest)
|
||||
.reduce(scheduleModel.zero(), Money::add);
|
||||
}
|
||||
|
||||
private List<RepaymentPeriod> findPossiblyOverdueRepaymentPeriods(final LocalDate targetDate,
|
||||
final ProgressiveLoanInterestScheduleModel model) {
|
||||
return model.repaymentPeriods().stream() //
|
||||
.filter(repaymentPeriod -> DateUtils.isAfter(targetDate, repaymentPeriod.getDueDate())).toList();
|
||||
}
|
||||
|
||||
private boolean adjustOverduePrincipal(final LocalDate currentDate, final RepaymentPeriod currentInstallment,
|
||||
final Money overduePrincipal, final Money aggregatedOverDuePrincipal, final ProgressiveTransactionCtx ctx) {
|
||||
final LocalDate fromDate = currentInstallment.getFromDate();
|
||||
final LocalDate toDate = currentInstallment.getDueDate();
|
||||
final ProgressiveLoanInterestScheduleModel model = ctx.getModel();
|
||||
final boolean prepayAttempt = ctx.isPrepayAttempt();
|
||||
final LoanInterestRecalculationDetails loanInterestRecalculationDetails = ctx.getInstallments().getFirst().getLoan()
|
||||
.getLoanInterestRecalculationDetails();
|
||||
|
||||
if (!currentDate.equals(model.lastOverdueBalanceChange())) {
|
||||
if (model.lastOverdueBalanceChange() == null || currentInstallment.getFromDate().isAfter(model.lastOverdueBalanceChange())) {
|
||||
addBalanceCorrection(model, fromDate, overduePrincipal);
|
||||
} else {
|
||||
addBalanceCorrection(model, model.lastOverdueBalanceChange(), overduePrincipal);
|
||||
}
|
||||
|
||||
if (currentDate.isAfter(fromDate) && !currentDate.isAfter(toDate)) {
|
||||
LocalDate lastOverdueBalanceChange;
|
||||
if (shouldRecalculateTillInstallmentDueDate(loanInterestRecalculationDetails, prepayAttempt)) {
|
||||
lastOverdueBalanceChange = toDate;
|
||||
} else {
|
||||
lastOverdueBalanceChange = currentDate;
|
||||
}
|
||||
addBalanceCorrection(model, lastOverdueBalanceChange, aggregatedOverDuePrincipal.negated());
|
||||
model.lastOverdueBalanceChange(lastOverdueBalanceChange);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldRecalculateTillInstallmentDueDate(final LoanInterestRecalculationDetails recalculationDetails,
|
||||
final boolean isPrepayAttempt) {
|
||||
// Rest frequency type and pre close interest calculation strategy can be controversial
|
||||
// if restFrequencyType == DAILY and preCloseInterestCalculationStrategy == TILL_PRE_CLOSURE_DATE
|
||||
// no problem. Calculate till transaction date
|
||||
// if restFrequencyType == SAME_AS_REPAYMENT_PERIOD and preCloseInterestCalculationStrategy ==
|
||||
// TILL_REST_FREQUENCY_DATE
|
||||
// again, no problem. Calculate till due date of current installment
|
||||
// if restFrequencyType == DAILY and preCloseInterestCalculationStrategy == TILL_REST_FREQUENCY_DATE
|
||||
// or restFrequencyType == SAME_AS_REPAYMENT_PERIOD and preCloseInterestCalculationStrategy ==
|
||||
// TILL_PRE_CLOSURE_DATE
|
||||
// we cannot harmonize the two configs. Behaviour should mimic prepay api.
|
||||
return switch (recalculationDetails.getRestFrequencyType()) {
|
||||
case DAILY ->
|
||||
isPrepayAttempt && recalculationDetails.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled();
|
||||
case SAME_AS_REPAYMENT_PERIOD ->
|
||||
recalculationDetails.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled();
|
||||
case WEEKLY -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: WEEKLY");
|
||||
case MONTHLY -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: MONTHLY");
|
||||
case INVALID -> throw new IllegalStateException("Unexpected RecalculationFrequencyType: INVALID");
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,18 +702,23 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
* the balances of the updated model and also recalculate the EMI if the EMI of the last repayment period differs
|
||||
* significantly from other periods.
|
||||
*/
|
||||
private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveTransactionCtx ctx,
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LoanTransaction loanTransaction) {
|
||||
final List<RepaymentPeriod> newPeriods = temporaryReAgedScheduleModel.repaymentPeriods();
|
||||
final ProgressiveLoanInterestScheduleModel scheduleModel = ctx.getModel();
|
||||
final Loan loan = loanTransaction.getLoan();
|
||||
|
||||
if (newPeriods.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<RepaymentPeriod> existingRepaymentPeriods = scheduleModel.repaymentPeriods();
|
||||
final RepaymentPeriod firstRepaymentPeriod = existingRepaymentPeriods.getFirst();
|
||||
final Money disbursedAmount = firstRepaymentPeriod.getTotalDisbursedAmount();
|
||||
|
||||
final LocalDate reAgingStartDate = loanTransaction.getLoanReAgeParameter().getStartDate();
|
||||
|
||||
final Optional<RepaymentPeriod> firstExistingRepaymentPeriodOpt = existingRepaymentPeriods.stream()
|
||||
final Optional<RepaymentPeriod> firstExistingReAgedRepaymentPeriodOpt = existingRepaymentPeriods.stream()
|
||||
.filter(period -> period.getDueDate().equals(reAgingStartDate)).findFirst();
|
||||
|
||||
for (final RepaymentPeriod newPeriod : newPeriods) {
|
||||
@@ -611,8 +726,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
period -> period.getFromDate().equals(newPeriod.getFromDate()) && period.getDueDate().equals(newPeriod.getDueDate()))
|
||||
.findFirst();
|
||||
Optional<RepaymentPeriod> previousExistingRepaymentPeriodOpt = Optional.empty();
|
||||
if (existingRepaymentPeriodOpt.isPresent() && firstExistingRepaymentPeriodOpt.isPresent()
|
||||
&& existingRepaymentPeriodOpt.get().equals(firstExistingRepaymentPeriodOpt.get())) {
|
||||
if (existingRepaymentPeriodOpt.isPresent() && firstExistingReAgedRepaymentPeriodOpt.isPresent()
|
||||
&& existingRepaymentPeriodOpt.get().equals(firstExistingReAgedRepaymentPeriodOpt.get())) {
|
||||
previousExistingRepaymentPeriodOpt = existingRepaymentPeriodOpt.get().getPrevious();
|
||||
}
|
||||
|
||||
@@ -623,25 +738,53 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
previousExistingRepaymentPeriodOpt.orElseGet(existingRepaymentPeriods::getLast), newPeriod.getFromDate(),
|
||||
newPeriod.getDueDate(), newPrincipal.add(newInterest), MoneyHelper.getMathContext(),
|
||||
loanTransaction.getLoan().getLoanProductRelatedDetail());
|
||||
rp.setTotalDisbursedAmount(scheduleModel.repaymentPeriods().getFirst().getTotalDisbursedAmount());
|
||||
rp.setTotalDisbursedAmount(disbursedAmount);
|
||||
|
||||
if (existingRepaymentPeriodOpt.isPresent()) {
|
||||
// Add an interest period on the disbursement date for the very first replaced period
|
||||
if (existingRepaymentPeriodOpt.get().equals(firstRepaymentPeriod)) {
|
||||
final LocalDate disbursementDate = loanTransaction.getLoan().getDisbursementDate();
|
||||
rp.getInterestPeriods().getFirst().setDisbursementAmount(disbursedAmount);
|
||||
rp.getInterestPeriods().getFirst().setFromDate(disbursementDate);
|
||||
rp.getInterestPeriods().getFirst().setDueDate(disbursementDate);
|
||||
rp.getInterestPeriods().add(InterestPeriod.withEmptyAmounts(rp, rp.getFromDate(), rp.getDueDate()));
|
||||
}
|
||||
existingRepaymentPeriods.remove(existingRepaymentPeriodOpt.get());
|
||||
}
|
||||
|
||||
existingRepaymentPeriodOpt.ifPresent(existingRepaymentPeriods::remove);
|
||||
existingRepaymentPeriods.add(rp);
|
||||
calculateRateFactorForRepaymentPeriod(rp, scheduleModel);
|
||||
}
|
||||
|
||||
final RepaymentPeriod lastReAgedInstallment = newPeriods.getLast();
|
||||
if (reAgingStartDate.isBefore(DateUtils.getBusinessLocalDate()) && isInterestRecalculationIsAllowed(ctx, loan)) {
|
||||
scheduleModel.lastOverdueBalanceChange(null);
|
||||
recalculateModelOverdueAmountsTillDate(ctx, loanTransaction.getSubmittedOnDate());
|
||||
}
|
||||
|
||||
final List<RepaymentPeriod> reAgedRepaymentPeriods = existingRepaymentPeriods.stream()
|
||||
.filter(repaymentPeriod -> (!repaymentPeriod.getFromDate().isBefore(reAgingStartDate)
|
||||
|| repaymentPeriod.getDueDate().isEqual(reAgingStartDate))
|
||||
&& !repaymentPeriod.getDueDate().isAfter(lastReAgedInstallment.getDueDate()))
|
||||
&& !repaymentPeriod.getDueDate().isAfter(newPeriods.getLast().getDueDate()))
|
||||
.toList();
|
||||
|
||||
// cleanup repayment periods after re-aging
|
||||
if (reAgedRepaymentPeriods.getLast() != null) {
|
||||
final List<RepaymentPeriod> repaymentPeriodsToRemove = existingRepaymentPeriods.stream()
|
||||
.filter(rp -> !rp.getFromDate().isBefore(reAgedRepaymentPeriods.getLast().getDueDate())).toList();
|
||||
repaymentPeriodsToRemove.forEach(existingRepaymentPeriods::remove);
|
||||
}
|
||||
|
||||
calculateOutstandingBalance(scheduleModel);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, loanTransaction.getTransactionDate());
|
||||
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, reAgedRepaymentPeriods);
|
||||
}
|
||||
|
||||
private boolean isInterestRecalculationIsAllowed(final ProgressiveTransactionCtx ctx, final Loan loan) {
|
||||
return loan.isInterestBearingAndInterestRecalculationEnabled() && !ctx.isChargedOff() && !ctx.isWrittenOff()
|
||||
&& !ctx.isContractTerminated() && !loan.isNpa()
|
||||
&& !loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue();
|
||||
}
|
||||
|
||||
/**
|
||||
* * Generates temporary interestScheduleModel with re-aged repayment periods
|
||||
*/
|
||||
@@ -663,8 +806,14 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
* is used to generate re-aged repayment periods
|
||||
*/
|
||||
@NotNull
|
||||
private static LocalDate calculateFirstReAgedPeriodStartDate(final LoanReAgeParameter loanReAgeParameter) {
|
||||
private static LocalDate calculateFirstReAgedPeriodStartDate(final LoanTransaction loanTransaction) {
|
||||
final LoanReAgeParameter loanReAgeParameter = loanTransaction.getLoanReAgeParameter();
|
||||
final LocalDate reAgingStartDate = loanReAgeParameter.getStartDate();
|
||||
|
||||
if (reAgingStartDate.isEqual(loanTransaction.getLoan().getDisbursementDate())) {
|
||||
return reAgingStartDate;
|
||||
}
|
||||
|
||||
return switch (loanReAgeParameter.getFrequencyType()) {
|
||||
case DAYS -> reAgingStartDate.minusDays(loanReAgeParameter.getFrequencyNumber());
|
||||
case WEEKS -> reAgingStartDate.minusWeeks(loanReAgeParameter.getFrequencyNumber());
|
||||
|
||||
+1
@@ -60,6 +60,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
private Money creditedPrincipal;
|
||||
/** Stores credited interest. Related transaction: Chargeback */
|
||||
private Money creditedInterest;
|
||||
@Setter
|
||||
private Money disbursementAmount;
|
||||
private Money balanceCorrectionAmount;
|
||||
private Money outstandingLoanBalance;
|
||||
|
||||
+1
@@ -76,6 +76,7 @@ public class LoanReAgingValidator {
|
||||
|
||||
LocalDate startDate = command.localDateValueOfParameterNamed(LoanReAgingApiConstants.startDate);
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
// validate re-age transaction occurs after or on the disbursement date
|
||||
baseDataValidator.reset().parameter(LoanReAgingApiConstants.startDate).value(startDate).notNull()
|
||||
.validateDateAfterOrEqual(loan.getDisbursementDate());
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user