FINERACT-2326: Add the ability to reschedule non-interest-bearing progressive loans to have interest
This commit is contained in:
+1
-2
@@ -75,8 +75,7 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
|
|||||||
: Integer.valueOf(rescheduleData.get(4));
|
: Integer.valueOf(rescheduleData.get(4));
|
||||||
Integer extraTerms = (rescheduleData.get(5) == null || "0".equals(rescheduleData.get(5))) ? null
|
Integer extraTerms = (rescheduleData.get(5) == null || "0".equals(rescheduleData.get(5))) ? null
|
||||||
: Integer.valueOf(rescheduleData.get(5));
|
: Integer.valueOf(rescheduleData.get(5));
|
||||||
BigDecimal newInterestRate = (rescheduleData.get(6) == null || "0".equals(rescheduleData.get(6))) ? null
|
BigDecimal newInterestRate = (rescheduleData.get(6) == null) ? null : new BigDecimal(rescheduleData.get(6));
|
||||||
: new BigDecimal(rescheduleData.get(6));
|
|
||||||
|
|
||||||
PostCreateRescheduleLoansRequest request = new PostCreateRescheduleLoansRequest()//
|
PostCreateRescheduleLoansRequest request = new PostCreateRescheduleLoansRequest()//
|
||||||
.loanId(loanId)//
|
.loanId(loanId)//
|
||||||
|
|||||||
@@ -6708,8 +6708,8 @@ Feature: Loan
|
|||||||
| 2 | 15 | 29 March 2024 | 24 March 2024 | 243.79 | 121.89 | 0.0 | 0.0 | 0.0 | 121.89 | 121.89 | 121.89 | 0.0 | 0.0 |
|
| 2 | 15 | 29 March 2024 | 24 March 2024 | 243.79 | 121.89 | 0.0 | 0.0 | 0.0 | 121.89 | 121.89 | 121.89 | 0.0 | 0.0 |
|
||||||
| | | 01 April 2024 | | 312.69 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 01 April 2024 | | 312.69 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 3 | 0 | 01 April 2024 | 01 April 2024 | 478.31 | 78.17 | 0.0 | 0.0 | 0.0 | 78.17 | 78.17 | 0.0 | 0.0 | 0.0 |
|
| 3 | 0 | 01 April 2024 | 01 April 2024 | 478.31 | 78.17 | 0.0 | 0.0 | 0.0 | 78.17 | 78.17 | 0.0 | 0.0 | 0.0 |
|
||||||
| 4 | 15 | 13 April 2024 | | 239.16 | 239.15 | 0.0 | 0.0 | 0.0 | 239.15 | 121.89 | 121.89 | 0.0 | 117.26 |
|
| 4 | 15 | 13 April 2024 | | 239.15 | 239.16 | 0.0 | 0.0 | 0.0 | 239.16 | 121.89 | 121.89 | 0.0 | 117.27 |
|
||||||
| 5 | 15 | 28 April 2024 | | 0.0 | 239.16 | 0.0 | 0.0 | 0.0 | 239.16 | 121.9 | 121.9 | 0.0 | 117.26 |
|
| 5 | 15 | 28 April 2024 | | 0.0 | 239.15 | 0.0 | 0.0 | 0.0 | 239.15 | 121.9 | 121.9 | 0.0 | 117.25 |
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| 800.27 | 0.0 | 0.0 | 0.0 | 800.27 | 565.75 | 365.68 | 0.0 | 234.52 |
|
| 800.27 | 0.0 | 0.0 | 0.0 | 800.27 | 565.75 | 365.68 | 0.0 | 234.52 |
|
||||||
@@ -6733,8 +6733,8 @@ Feature: Loan
|
|||||||
| 2 | 15 | 29 March 2024 | 24 March 2024 | 243.79 | 121.89 | 0.0 | 0.0 | 0.0 | 121.89 | 121.89 | 121.89 | 0.0 | 0.0 |
|
| 2 | 15 | 29 March 2024 | 24 March 2024 | 243.79 | 121.89 | 0.0 | 0.0 | 0.0 | 121.89 | 121.89 | 121.89 | 0.0 | 0.0 |
|
||||||
| | | 01 April 2024 | | 312.69 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 01 April 2024 | | 312.69 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 3 | 0 | 01 April 2024 | 01 April 2024 | 478.31 | 78.17 | 0.0 | 0.0 | 0.0 | 78.17 | 78.17 | 0.0 | 0.0 | 0.0 |
|
| 3 | 0 | 01 April 2024 | 01 April 2024 | 478.31 | 78.17 | 0.0 | 0.0 | 0.0 | 78.17 | 78.17 | 0.0 | 0.0 | 0.0 |
|
||||||
| 4 | 15 | 13 April 2024 | 10 April 2024 | 239.16 | 239.15 | 0.0 | 0.0 | 0.0 | 239.15 | 239.15 | 239.15 | 0.0 | 0.0 |
|
| 4 | 15 | 13 April 2024 | 10 April 2024 | 239.15 | 239.16 | 0.0 | 0.0 | 0.0 | 239.16 | 239.16 | 239.16 | 0.0 | 0.0 |
|
||||||
| 5 | 15 | 28 April 2024 | 10 April 2024 | 0.0 | 239.16 | 0.0 | 0.0 | 0.0 | 239.16 | 239.16 | 239.16 | 0.0 | 0.0 |
|
| 5 | 15 | 28 April 2024 | 10 April 2024 | 0.0 | 239.15 | 0.0 | 0.0 | 0.0 | 239.15 | 239.15 | 239.15 | 0.0 | 0.0 |
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| 800.27 | 0.0 | 0.0 | 0.0 | 800.27 | 800.27 | 600.2 | 0.0 | 0.0 |
|
| 800.27 | 0.0 | 0.0 | 0.0 | 800.27 | 800.27 | 600.2 | 0.0 | 0.0 |
|
||||||
|
|||||||
@@ -1788,4 +1788,4 @@ Feature: Credit Balance Refund
|
|||||||
| 25 March 2025 | Credit Balance Refund | 122.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
| 25 March 2025 | Credit Balance Refund | 122.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 01 April 2025 | Disbursement | 243.79 | 0.0 | 0.0 | 0.0 | 0.0 | 243.79 | false | false |
|
| 01 April 2025 | Disbursement | 243.79 | 0.0 | 0.0 | 0.0 | 0.0 | 243.79 | false | false |
|
||||||
| 01 April 2025 | Down Payment | 61.0 | 61.0 | 0.0 | 0.0 | 0.0 | 182.79 | false | false |
|
| 01 April 2025 | Down Payment | 61.0 | 61.0 | 0.0 | 0.0 | 0.0 | 182.79 | false | false |
|
||||||
Then Loan status will be "ACTIVE"
|
Then Loan status will be "ACTIVE"
|
||||||
|
|||||||
@@ -917,141 +917,92 @@ Feature: LoanReschedule
|
|||||||
And Loan Transactions tab has the following data:
|
And Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| 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 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
|
|
||||||
@TestRailId:C3809
|
@TestRailId:C3882
|
||||||
Scenario: Verify change interest rate with 0->4 reschedule scenario - UC1
|
Scenario: Reschedule progressive loan to 0 percent interest then back to 10 percent
|
||||||
When Admin sets the business date to "01 April 2025"
|
When Admin sets the business date to "1 January 2024"
|
||||||
When Admin creates a client with random data
|
When Admin creates a client with random data
|
||||||
When Admin creates a fully customized loan with the following 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 |
|
| 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_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE | 01 April 2025 | 1000 | 0 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 4 | MONTHS | 1 | MONTHS | 4 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
| LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30 | 1 January 2024 | 100 | 10 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
And Admin successfully approves the loan on "01 April 2025" with "1000" amount and expected disbursement date on "01 April 2025"
|
And Admin successfully approves the loan on "1 January 2024" with "100" amount and expected disbursement date on "1 January 2024"
|
||||||
When Admin successfully disburse the loan on "01 April 2025" with "1000" EUR transaction amount
|
When Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||||
Then Loan Repayment schedule has 4 periods, with the following data for periods:
|
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 |
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| | | 01 April 2025 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 1 | 30 | 01 May 2025 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
| 1 | 31 | 01 February 2024 | | 83.67 | 16.33 | 0.83 | 0.0 | 0.0 | 17.16 | 0.0 | 0.0 | 0.0 | 17.16 |
|
||||||
| 2 | 31 | 01 June 2025 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
| 2 | 29 | 01 March 2024 | | 67.21 | 16.46 | 0.7 | 0.0 | 0.0 | 17.16 | 0.0 | 0.0 | 0.0 | 17.16 |
|
||||||
| 3 | 30 | 01 July 2025 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
| 3 | 31 | 01 April 2024 | | 50.61 | 16.6 | 0.56 | 0.0 | 0.0 | 17.16 | 0.0 | 0.0 | 0.0 | 17.16 |
|
||||||
| 4 | 31 | 01 August 2025 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
| 4 | 30 | 01 May 2024 | | 33.87 | 16.74 | 0.42 | 0.0 | 0.0 | 17.16 | 0.0 | 0.0 | 0.0 | 17.16 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.99 | 16.88 | 0.28 | 0.0 | 0.0 | 17.16 | 0.0 | 0.0 | 0.0 | 17.16 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.99 | 0.14 | 0.0 | 0.0 | 17.13 | 0.0 | 0.0 | 0.0 | 17.13 |
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
| 100.0 | 2.93 | 0.0 | 0.0 | 102.93 | 0.0 | 0.0 | 0.0 | 102.93 |
|
||||||
Then Loan Transactions tab has the following data:
|
When Admin creates and approves Loan reschedule with the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||||
| 01 April 2025 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
|
| 02 January 2024 | 01 January 2024 | | | | | 0 |
|
||||||
When Admin sets the business date to "01 May 2025"
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
And Admin runs inline COB job for Loan
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
And Customer makes "AUTOPAY" repayment on "01 May 2025" with 250.0 EUR transaction amount
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
Then Loan Repayment schedule has 4 periods, with the following data for periods:
|
| 1 | 31 | 01 February 2024 | | 83.33 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| 2 | 29 | 01 March 2024 | | 66.66 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| | | 01 April 2025 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| 3 | 31 | 01 April 2024 | | 49.99 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| 1 | 30 | 01 May 2025 | 01 May 2025 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
|
| 4 | 30 | 01 May 2024 | | 33.32 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| 2 | 31 | 01 June 2025 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
| 5 | 31 | 01 June 2024 | | 16.65 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| 3 | 30 | 01 July 2025 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.65 | 0.0 | 0.0 | 0.0 | 16.65 | 0.0 | 0.0 | 0.0 | 16.65 |
|
||||||
| 4 | 31 | 01 August 2025 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0 | 0.0 | 0.0 | 750.0 |
|
| 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 |
|
||||||
Then Loan Transactions tab has the following data:
|
When Admin creates and approves Loan reschedule with the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||||
| 01 April 2025 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
|
| 03 January 2024 | 01 January 2024 | | | | | 10 |
|
||||||
| 01 May 2025 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false | false |
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
When Admin sets the business date to "05 May 2025"
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
And Admin runs inline COB job for Loan
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
# --- Loan reschedule: Interest rate modification effective from next day ---
|
| 1 | 31 | 01 February 2024 | | 83.66 | 16.34 | 0.81 | 0.0 | 0.0 | 17.15 | 0.0 | 0.0 | 0.0 | 17.15 |
|
||||||
#When Admin attempt Loan reschedule with changing interest rate to zero following data validation error should occur
|
| 2 | 29 | 01 March 2024 | | 67.21 | 16.45 | 0.7 | 0.0 | 0.0 | 17.15 | 0.0 | 0.0 | 0.0 | 17.15 |
|
||||||
Then Loan reschedule with the following data results a 400 error and "LOAN_RESCHEDULE_NOT_ALLOWED_FROM_ZERO_TO_NEW_INTEREST_RATE" error message
|
| 3 | 31 | 01 April 2024 | | 50.62 | 16.59 | 0.56 | 0.0 | 0.0 | 17.15 | 0.0 | 0.0 | 0.0 | 17.15 |
|
||||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
| 4 | 30 | 01 May 2024 | | 33.89 | 16.73 | 0.42 | 0.0 | 0.0 | 17.15 | 0.0 | 0.0 | 0.0 | 17.15 |
|
||||||
| 06 May 2025 | 05 May 2025 | | | | | 4 |
|
| 5 | 31 | 01 June 2024 | | 17.02 | 16.87 | 0.28 | 0.0 | 0.0 | 17.15 | 0.0 | 0.0 | 0.0 | 17.15 |
|
||||||
When Admin sets the business date to "06 May 2025"
|
| 6 | 30 | 01 July 2024 | | 0.0 | 17.02 | 0.14 | 0.0 | 0.0 | 17.16 | 0.0 | 0.0 | 0.0 | 17.16 |
|
||||||
And Customer makes "AUTOPAY" repayment on "06 May 2025" with 100.0 EUR transaction amount
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
# TODO uncomment and update repayment schedule after fixing PS-2665
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
# Then Loan Repayment schedule has 4 periods, with the following data for periods:
|
| 100.0 | 2.91 | 0.0 | 0.0 | 102.91 | 0.0 | 0.0 | 0.0 | 102.91 |
|
||||||
# | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
|
||||||
# | | | 01 April 2025 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
|
||||||
# | 1 | 30 | 01 May 2025 | 01 May 2025 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
|
|
||||||
# | 2 | 31 | 01 June 2025 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 100.0 | 100.0 | 0.0 | 150.0 |
|
|
||||||
# | 3 | 30 | 01 July 2025 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
|
||||||
# | 4 | 31 | 01 August 2025 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
|
||||||
# Then Loan Repayment schedule has the following data in Total row:
|
|
||||||
# | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
|
||||||
# | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 350.0 | 100.0 | 0.0 | 650.0 |
|
|
||||||
Then Loan Transactions tab has the following data:
|
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
|
||||||
| 01 April 2025 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
|
|
||||||
| 01 May 2025 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false | false |
|
|
||||||
| 06 May 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 650.0 | false | false |
|
|
||||||
Then Loan status will be "ACTIVE"
|
|
||||||
|
|
||||||
@TestRailId:C3810
|
@TestRailId:C3883
|
||||||
Scenario: Verify change interest rate with 4->0 reschedule scenario - UC2
|
Scenario: Create progressive loan with 0 percent interest then to 10 percent after first period
|
||||||
When Admin sets the business date to "01 April 2025"
|
When Admin sets the business date to "1 January 2024"
|
||||||
When Admin creates a client with random data
|
When Admin creates a client with random data
|
||||||
When Admin creates a fully customized loan with the following 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 |
|
| 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_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE | 01 April 2025 | 1000 | 4 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 4 | MONTHS | 1 | MONTHS | 4 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
| LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30 | 1 January 2024 | 100 | 0 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
And Admin successfully approves the loan on "01 April 2025" with "1000" amount and expected disbursement date on "01 April 2025"
|
And Admin successfully approves the loan on "1 January 2024" with "100" amount and expected disbursement date on "1 January 2024"
|
||||||
When Admin successfully disburse the loan on "01 April 2025" with "1000" EUR transaction amount
|
When Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount
|
||||||
Then Loan Repayment schedule has 4 periods, with the following data for periods:
|
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 |
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| | | 01 April 2025 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 1 | 30 | 01 May 2025 | | 751.24 | 248.76 | 3.33 | 0.0 | 0.0 | 252.09 | 0.0 | 0.0 | 0.0 | 252.09 |
|
| 1 | 31 | 01 February 2024 | | 83.33 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| 2 | 31 | 01 June 2025 | | 501.65 | 249.59 | 2.5 | 0.0 | 0.0 | 252.09 | 0.0 | 0.0 | 0.0 | 252.09 |
|
| 2 | 29 | 01 March 2024 | | 66.66 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| 3 | 30 | 01 July 2025 | | 251.23 | 250.42 | 1.67 | 0.0 | 0.0 | 252.09 | 0.0 | 0.0 | 0.0 | 252.09 |
|
| 3 | 31 | 01 April 2024 | | 49.99 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| 4 | 31 | 01 August 2025 | | 0.0 | 251.23 | 0.84 | 0.0 | 0.0 | 252.07 | 0.0 | 0.0 | 0.0 | 252.07 |
|
| 4 | 30 | 01 May 2024 | | 33.32 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
|
| 5 | 31 | 01 June 2024 | | 16.65 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.65 | 0.0 | 0.0 | 0.0 | 16.65 | 0.0 | 0.0 | 0.0 | 16.65 |
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| 1000.0 | 8.34 | 0.0 | 0.0 | 1008.34 | 0.0 | 0.0 | 0.0 | 1008.34 |
|
| 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 |
|
||||||
Then Loan Transactions tab has the following data:
|
When Admin creates and approves Loan reschedule with the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||||
| 01 April 2025 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
|
| 02 February 2024 | 01 January 2024 | | | | | 10 |
|
||||||
When Admin sets the business date to "01 May 2025"
|
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||||
And Admin runs inline COB job for Loan
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
And Customer makes "AUTOPAY" repayment on "01 May 2025" with 252.09 EUR transaction amount
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
Then Loan Repayment schedule has 4 periods, with the following data for periods:
|
| 1 | 31 | 01 February 2024 | | 83.33 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 | 0.0 | 0.0 | 0.0 | 16.67 |
|
||||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| 2 | 29 | 01 March 2024 | | 66.94 | 16.39 | 0.69 | 0.0 | 0.0 | 17.08 | 0.0 | 0.0 | 0.0 | 17.08 |
|
||||||
| | | 01 April 2025 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| 3 | 31 | 01 April 2024 | | 50.42 | 16.52 | 0.56 | 0.0 | 0.0 | 17.08 | 0.0 | 0.0 | 0.0 | 17.08 |
|
||||||
| 1 | 30 | 01 May 2025 | 01 May 2025 | 751.24 | 248.76 | 3.33 | 0.0 | 0.0 | 252.09 | 252.09 | 0.0 | 0.0 | 0.0 |
|
| 4 | 30 | 01 May 2024 | | 33.76 | 16.66 | 0.42 | 0.0 | 0.0 | 17.08 | 0.0 | 0.0 | 0.0 | 17.08 |
|
||||||
| 2 | 31 | 01 June 2025 | | 501.65 | 249.59 | 2.5 | 0.0 | 0.0 | 252.09 | 0.0 | 0.0 | 0.0 | 252.09 |
|
| 5 | 31 | 01 June 2024 | | 16.96 | 16.8 | 0.28 | 0.0 | 0.0 | 17.08 | 0.0 | 0.0 | 0.0 | 17.08 |
|
||||||
| 3 | 30 | 01 July 2025 | | 251.23 | 250.42 | 1.67 | 0.0 | 0.0 | 252.09 | 0.0 | 0.0 | 0.0 | 252.09 |
|
| 6 | 30 | 01 July 2024 | | 0.0 | 16.96 | 0.14 | 0.0 | 0.0 | 17.1 | 0.0 | 0.0 | 0.0 | 17.1 |
|
||||||
| 4 | 31 | 01 August 2025 | | 0.0 | 251.23 | 0.84 | 0.0 | 0.0 | 252.07 | 0.0 | 0.0 | 0.0 | 252.07 |
|
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| 1000.0 | 8.34 | 0.0 | 0.0 | 1008.34 | 252.09 | 0.0 | 0.0 | 756.25 |
|
| 100.0 | 2.09 | 0.0 | 0.0 | 102.09 | 0.0 | 0.0 | 0.0 | 102.09 |
|
||||||
Then Loan Transactions tab has the following data:
|
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
|
||||||
| 01 April 2025 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
|
|
||||||
| 30 April 2025 | Accrual | 3.22 | 0.0 | 3.22 | 0.0 | 0.0 | 0.0 | false | false |
|
|
||||||
| 01 May 2025 | Repayment | 252.09 | 248.76 | 3.33 | 0.0 | 0.0 | 751.24 | false | false |
|
|
||||||
When Admin sets the business date to "05 May 2025"
|
|
||||||
And Admin runs inline COB job for Loan
|
|
||||||
# --- Loan reschedule: Interest rate modification effective from next day ---
|
|
||||||
#When Admin attempt Loan reschedule with changing interest rate from zero to 4 following data validation error should occur
|
|
||||||
Then Loan reschedule with the following data results a 400 error and "LOAN_RESCHEDULE_NOT_ALLOWED_FROM_CURRENT_INTEREST_RATE_TO_ZERO" error message
|
|
||||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
|
||||||
| 06 May 2025 | 05 May 2025 | | | | | 0 |
|
|
||||||
When Admin sets the business date to "06 May 2025"
|
|
||||||
And Customer makes "AUTOPAY" repayment on "06 May 2025" with 100.0 EUR transaction amount
|
|
||||||
# TODO uncomment and update repayment schedule after fixing PS-2634
|
|
||||||
# Then Loan Repayment schedule has 4 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 April 2025 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
|
||||||
# | 1 | 30 | 01 May 2025 | 01 May 2025 | 751.24 | 248.76 | 3.33 | 0.0 | 0.0 | 252.09 | 252.09 | 0.0 | 0.0 | 0.0 |
|
|
||||||
# | 2 | 31 | 01 June 2025 | | 501.37 | 249.87 | 2.22 | 0.0 | 0.0 | 252.09 | 100.0 | 100.0 | 0.0 | 152.09 |
|
|
||||||
# | 3 | 30 | 01 July 2025 | | 250.95 | 250.42 | 1.67 | 0.0 | 0.0 | 252.09 | 0.0 | 0.0 | 0.0 | 252.09 |
|
|
||||||
# | 4 | 31 | 01 August 2025 | | 0.0 | 250.95 | 0.84 | 0.0 | 0.0 | 251.79 | 0.0 | 0.0 | 0.0 | 251.79 |
|
|
||||||
# Then Loan Repayment schedule has the following data in Total row:
|
|
||||||
# | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
|
||||||
# | 1000.0 | 8.06 | 0.0 | 0.0 | 1008.06 | 352.09 | 100.0 | 0.0 | 655.97 |
|
|
||||||
Then Loan Transactions tab has the following data:
|
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
|
||||||
| 01 April 2025 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
|
|
||||||
| 30 April 2025 | Accrual | 3.22 | 0.0 | 3.22 | 0.0 | 0.0 | 0.0 | false | false |
|
|
||||||
| 01 May 2025 | Repayment | 252.09 | 248.76 | 3.33 | 0.0 | 0.0 | 751.24 | false | false |
|
|
||||||
| 01 May 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
|
|
||||||
| 02 May 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
|
||||||
| 03 May 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
|
||||||
| 04 May 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
|
||||||
| 06 May 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 651.24 | false | false |
|
|
||||||
Then Loan status will be "ACTIVE"
|
|
||||||
|
|||||||
+23
-5
@@ -507,7 +507,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
MoneyHolder overpaymentHolder = ctx.getOverpaymentHolder();
|
MoneyHolder overpaymentHolder = ctx.getOverpaymentHolder();
|
||||||
|
|
||||||
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||||
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
|
&& loanTransaction.getLoan().isInterestRecalculationEnabled()) {
|
||||||
var model = progressiveTransactionCtx.getModel();
|
var model = progressiveTransactionCtx.getModel();
|
||||||
|
|
||||||
// Copy and paste Logic from super.handleCreditBalanceRefund
|
// Copy and paste Logic from super.handleCreditBalanceRefund
|
||||||
@@ -778,7 +778,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void processCreditTransaction(LoanTransaction loanTransaction, TransactionCtx ctx) {
|
protected void processCreditTransaction(LoanTransaction loanTransaction, TransactionCtx ctx) {
|
||||||
if (loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loanTransaction.getLoan().isInterestRecalculationEnabled()) {
|
||||||
processCreditTransactionWithEmiCalculator(loanTransaction, (ProgressiveTransactionCtx) ctx);
|
processCreditTransactionWithEmiCalculator(loanTransaction, (ProgressiveTransactionCtx) ctx);
|
||||||
} else if (hasNoCustomCreditAllocationRule(loanTransaction)) {
|
} else if (hasNoCustomCreditAllocationRule(loanTransaction)) {
|
||||||
super.processCreditTransaction(loanTransaction, ctx.getOverpaymentHolder(), ctx.getCurrency(), ctx.getInstallments());
|
super.processCreditTransaction(loanTransaction, ctx.getOverpaymentHolder(), ctx.getCurrency(), ctx.getInstallments());
|
||||||
@@ -1224,8 +1224,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
|
|
||||||
private void handleDisbursement(LoanTransaction disbursementTransaction, TransactionCtx transactionCtx) {
|
private void handleDisbursement(LoanTransaction disbursementTransaction, TransactionCtx transactionCtx) {
|
||||||
// TODO: Fix this and enhance EMICalculator to support reamortization and reaging
|
// TODO: Fix this and enhance EMICalculator to support reamortization and reaging
|
||||||
final Loan loan = disbursementTransaction.getLoan();
|
if (shouldUseEmiCalculation(transactionCtx, disbursementTransaction.getTransactionDate())) {
|
||||||
if (loan.isInterestBearing()) {
|
|
||||||
handleDisbursementWithEMICalculator(disbursementTransaction, transactionCtx);
|
handleDisbursementWithEMICalculator(disbursementTransaction, transactionCtx);
|
||||||
} else {
|
} else {
|
||||||
handleDisbursementWithoutEMICalculator(disbursementTransaction, transactionCtx);
|
handleDisbursementWithoutEMICalculator(disbursementTransaction, transactionCtx);
|
||||||
@@ -1356,7 +1355,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleCapitalizedIncome(LoanTransaction capitalizedIncomeTransaction, TransactionCtx transactionCtx) {
|
private void handleCapitalizedIncome(LoanTransaction capitalizedIncomeTransaction, TransactionCtx transactionCtx) {
|
||||||
if (capitalizedIncomeTransaction.getLoan().isInterestBearing()) {
|
// TODO: Fix this and enhance EMICalculator to support reamortization and reaging
|
||||||
|
if (shouldUseEmiCalculation(transactionCtx, capitalizedIncomeTransaction.getTransactionDate())) {
|
||||||
handleCapitalizedIncomeWithEMICalculator(capitalizedIncomeTransaction, transactionCtx);
|
handleCapitalizedIncomeWithEMICalculator(capitalizedIncomeTransaction, transactionCtx);
|
||||||
} else {
|
} else {
|
||||||
handleCapitalizedIncomeWithoutEMICalculator(capitalizedIncomeTransaction, transactionCtx);
|
handleCapitalizedIncomeWithoutEMICalculator(capitalizedIncomeTransaction, transactionCtx);
|
||||||
@@ -1476,6 +1476,24 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldUseEmiCalculation(TransactionCtx transactionCtx, LocalDate transactionDate) {
|
||||||
|
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx) {
|
||||||
|
boolean hasActiveReAmortization = progressiveTransactionCtx.getAlreadyProcessedTransactions().stream()
|
||||||
|
.anyMatch(t -> t.getTypeOf().isReAmortize() && t.isNotReversed());
|
||||||
|
boolean hasActiveReAge = progressiveTransactionCtx.getAlreadyProcessedTransactions().stream()
|
||||||
|
.anyMatch(t -> t.getTypeOf().isReAge() && t.isNotReversed());
|
||||||
|
if (hasActiveReAmortization) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return !hasActiveReAge || !DateUtils.isAfter(transactionDate, progressiveTransactionCtx.getModel().getMaturityDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// From now on we are defaulting to using the EMICalculator on all progressive loans. However currently the
|
||||||
|
// model is not aware of re-aging and re-amortization. So only these specific cases should ignore this
|
||||||
|
// requirement. This method can be removed once these operations are supported by the EMI model.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected void handleWriteOff(final LoanTransaction transaction, TransactionCtx ctx) {
|
protected void handleWriteOff(final LoanTransaction transaction, TransactionCtx ctx) {
|
||||||
super.handleWriteOff(transaction, ctx.getCurrency(), ctx.getInstallments());
|
super.handleWriteOff(transaction, ctx.getCurrency(), ctx.getInstallments());
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -24,7 +24,6 @@ import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.Loan
|
|||||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateAndRetrieveRescheduleFromDate;
|
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateAndRetrieveRescheduleFromDate;
|
||||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateApprovalDate;
|
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateApprovalDate;
|
||||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateForOverdueCharges;
|
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateForOverdueCharges;
|
||||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateInterestRate;
|
|
||||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateLoanIsActive;
|
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateLoanIsActive;
|
||||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateRescheduleReasonComment;
|
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateRescheduleReasonComment;
|
||||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateRescheduleReasonId;
|
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateRescheduleReasonId;
|
||||||
@@ -78,8 +77,7 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
|||||||
validateRescheduleReasonId(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
validateRescheduleReasonId(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
||||||
validateRescheduleReasonComment(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
validateRescheduleReasonComment(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
||||||
LocalDate adjustedDueDate = validateAndRetrieveAdjustedDate(fromJsonHelper, jsonElement, rescheduleFromDate, dataValidatorBuilder);
|
LocalDate adjustedDueDate = validateAndRetrieveAdjustedDate(fromJsonHelper, jsonElement, rescheduleFromDate, dataValidatorBuilder);
|
||||||
BigDecimal interestRate = validateInterestRate(loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate(), fromJsonHelper,
|
BigDecimal interestRate = validateInterestRate(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
||||||
jsonElement, dataValidatorBuilder);
|
|
||||||
validateUnsupportedParams(jsonElement, dataValidatorBuilder);
|
validateUnsupportedParams(jsonElement, dataValidatorBuilder);
|
||||||
|
|
||||||
boolean hasInterestRateChange = interestRate != null;
|
boolean hasInterestRateChange = interestRate != null;
|
||||||
@@ -197,4 +195,13 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
|||||||
"Interest rate change for the provided date is already exists.", rescheduleFromDate);
|
"Interest rate change for the provided date is already exists.", rescheduleFromDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal validateInterestRate(final FromJsonHelper fromJsonHelper, final JsonElement jsonElement,
|
||||||
|
DataValidatorBuilder dataValidatorBuilder) {
|
||||||
|
final BigDecimal interestRate = fromJsonHelper
|
||||||
|
.extractBigDecimalWithLocaleNamed(RescheduleLoansApiConstants.newInterestRateParamName, jsonElement);
|
||||||
|
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.newInterestRateParamName).value(interestRate).ignoreIfNull()
|
||||||
|
.zeroOrPositiveAmount();
|
||||||
|
return interestRate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -182,7 +182,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction transaction) {
|
private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction transaction) {
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
|
if (loan.isInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
} else {
|
} else {
|
||||||
reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
|
reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
|
||||||
|
|||||||
+8
-2
@@ -161,6 +161,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addCapitalizedIncome(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
private void addCapitalizedIncome(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
||||||
|
scheduleModel.repaymentPeriods().stream().filter(rp -> !operation.getSubmittedOnDate().isAfter(rp.getFromDate()))
|
||||||
|
.forEach(rp -> rp.setTotalCapitalizedIncomeAmount(
|
||||||
|
MathUtil.nullToZero(rp.getTotalCapitalizedIncomeAmount()).add(operation.getAmount().getAmount())));
|
||||||
|
|
||||||
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), scheduleModel.zero(),
|
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), scheduleModel.zero(),
|
||||||
scheduleModel.zero(), operation.getAmount()).ifPresent((repaymentPeriod) -> {
|
scheduleModel.zero(), operation.getAmount()).ifPresent((repaymentPeriod) -> {
|
||||||
calculateEMIValueAndRateFactors(
|
calculateEMIValueAndRateFactors(
|
||||||
@@ -920,9 +924,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
|
|
||||||
// already repaid principals should be subtracted from total disbursed amount to calculate correct EMI.
|
// already repaid principals should be subtracted from total disbursed amount to calculate correct EMI.
|
||||||
BigDecimal alreadyRepaidPrincipals = first.getPrevious()
|
BigDecimal alreadyRepaidPrincipals = first.getPrevious()
|
||||||
.map(rp -> rp.calculateTotalDisbursedAmountTillGivenPeriod(null).subtract(rp.getOutstandingLoanBalance().getAmount()))
|
.map(rp -> rp.calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(null)
|
||||||
|
.subtract(rp.getOutstandingLoanBalance().getAmount()))
|
||||||
.orElse(BigDecimal.ZERO);
|
.orElse(BigDecimal.ZERO);
|
||||||
Money total = Money.of(currency, first.calculateTotalDisbursedAmountTillGivenPeriod(first.getLastInterestPeriod()))
|
Money total = Money
|
||||||
|
.of(currency, first.calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(first.getLastInterestPeriod()))
|
||||||
.plus(sumOfInterest).minus(alreadyRepaidPrincipals);
|
.plus(sumOfInterest).minus(alreadyRepaidPrincipals);
|
||||||
|
|
||||||
Money periodEmi = total.dividedBy(repaymentPeriods.size(), mc);
|
Money periodEmi = total.dividedBy(repaymentPeriods.size(), mc);
|
||||||
|
|||||||
+1
-1
@@ -150,7 +150,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
BigDecimal baseAmount = switch (method) {
|
BigDecimal baseAmount = switch (method) {
|
||||||
case FLAT -> MathUtil.nullToZero(getRepaymentPeriod().calculateTotalDisbursedAmountTillGivenPeriod(this));
|
case FLAT -> MathUtil.nullToZero(getRepaymentPeriod().calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(this));
|
||||||
case DECLINING_BALANCE -> getOutstandingLoanBalance().getAmount();
|
case DECLINING_BALANCE -> getOutstandingLoanBalance().getAmount();
|
||||||
default -> throw new UnsupportedOperationException("Method not implemented: " + method);
|
default -> throw new UnsupportedOperationException("Method not implemented: " + method);
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-5
@@ -84,6 +84,10 @@ public final class RepaymentPeriod {
|
|||||||
@Getter
|
@Getter
|
||||||
private BigDecimal totalDisbursedAmount;
|
private BigDecimal totalDisbursedAmount;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private BigDecimal totalCapitalizedIncomeAmount;
|
||||||
|
|
||||||
@JsonExclude
|
@JsonExclude
|
||||||
@Getter
|
@Getter
|
||||||
private final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail;
|
private final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail;
|
||||||
@@ -389,16 +393,21 @@ public final class RepaymentPeriod {
|
|||||||
/**
|
/**
|
||||||
* @param tillPeriod
|
* @param tillPeriod
|
||||||
* can be null. if null it calculates total disbursement including last interest period.
|
* can be null. if null it calculates total disbursement including last interest period.
|
||||||
* @return disbursed amount til interest period.
|
* @return disbursed and capitalized income amount till interest period.
|
||||||
*/
|
*/
|
||||||
public BigDecimal calculateTotalDisbursedAmountTillGivenPeriod(InterestPeriod tillPeriod) {
|
public BigDecimal calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(InterestPeriod tillPeriod) {
|
||||||
BigDecimal res = MathUtil.nullToZero(getTotalDisbursedAmount());
|
BigDecimal res = MathUtil.nullToZero(getTotalDisbursedAmount()).add(MathUtil.nullToZero(getTotalCapitalizedIncomeAmount()));
|
||||||
for (InterestPeriod interestPeriod : this.interestPeriods) {
|
for (InterestPeriod interestPeriod : this.interestPeriods) {
|
||||||
if (interestPeriod.equals(tillPeriod)) {
|
if (interestPeriod.equals(tillPeriod)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!interestPeriod.getDueDate().equals(getFromDate()) && interestPeriod.getDisbursementAmount() != null) {
|
if (!interestPeriod.getDueDate().equals(getFromDate())) {
|
||||||
res = res.add(interestPeriod.getDisbursementAmount().getAmount());
|
if (interestPeriod.getDisbursementAmount() != null) {
|
||||||
|
res = res.add(interestPeriod.getDisbursementAmount().getAmount());
|
||||||
|
}
|
||||||
|
if (interestPeriod.getCapitalizedIncomePrincipal() != null) {
|
||||||
|
res = res.add(interestPeriod.getCapitalizedIncomePrincipal().getAmount());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
-1
@@ -535,7 +535,6 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
|||||||
|
|
||||||
Loan loan = mock(Loan.class);
|
Loan loan = mock(Loan.class);
|
||||||
when(loan.getLoanRepaymentScheduleDetail()).thenReturn(loanProductRelatedDetail);
|
when(loan.getLoanRepaymentScheduleDetail()).thenReturn(loanProductRelatedDetail);
|
||||||
when(loan.isInterestBearing()).thenReturn(true);
|
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment installment1 = spy(
|
LoanRepaymentScheduleInstallment installment1 = spy(
|
||||||
new LoanRepaymentScheduleInstallment(loan, 1, disbursementDate, disbursementDate.plusMonths(1), BigDecimal.valueOf(200.0),
|
new LoanRepaymentScheduleInstallment(loan, 1, disbursementDate, disbursementDate.plusMonths(1), BigDecimal.valueOf(200.0),
|
||||||
|
|||||||
+6
-4
@@ -95,16 +95,18 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
|||||||
AdvancedPaymentScheduleTransactionProcessor advancedProcessor, Loan loan, LoanTransaction loanTransaction) {
|
AdvancedPaymentScheduleTransactionProcessor advancedProcessor, Loan loan, LoanTransaction loanTransaction) {
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan,
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan,
|
||||||
loanTransaction.getTransactionDate());
|
loanTransaction.getTransactionDate());
|
||||||
|
ProgressiveLoanInterestScheduleModel model = savedModel
|
||||||
|
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
|
||||||
|
|
||||||
ProgressiveTransactionCtx progressiveContext = new ProgressiveTransactionCtx(loan.getCurrency(),
|
ProgressiveTransactionCtx progressiveContext = new ProgressiveTransactionCtx(loan.getCurrency(),
|
||||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
||||||
new ChangedTransactionDetail(), savedModel.orElse(null), getTotalRefundInterestAmount(loan));
|
new ChangedTransactionDetail(), model, getTotalRefundInterestAmount(loan));
|
||||||
progressiveContext.getAlreadyProcessedTransactions().addAll(loanTransactionService.retrieveListOfTransactionsForReprocessing(loan));
|
progressiveContext.getAlreadyProcessedTransactions().addAll(loanTransactionService.retrieveListOfTransactionsForReprocessing(loan));
|
||||||
progressiveContext.setChargedOff(loan.isChargedOff());
|
progressiveContext.setChargedOff(loan.isChargedOff());
|
||||||
progressiveContext.setContractTerminated(loan.isContractTermination());
|
progressiveContext.setContractTerminated(loan.isContractTermination());
|
||||||
ChangedTransactionDetail result = advancedProcessor.processLatestTransaction(loanTransaction, progressiveContext);
|
ChangedTransactionDetail result = advancedProcessor.processLatestTransaction(loanTransaction, progressiveContext);
|
||||||
if (savedModel.isPresent() && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
|
if (!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
|
||||||
modelRepository.writeInterestScheduleModel(loan, savedModel.get());
|
modelRepository.writeInterestScheduleModel(loan, model);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -124,7 +126,7 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
|||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(
|
||||||
transactionProcessingStrategyCode);
|
transactionProcessingStrategyCode);
|
||||||
if (loanRepaymentScheduleTransactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedProcessor
|
if (loanRepaymentScheduleTransactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedProcessor
|
||||||
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
|
&& loanTransaction.getLoan().isInterestRecalculationEnabled()) {
|
||||||
return processLatestTransactionProgressiveInterestRecalculation(advancedProcessor, loanTransaction.getLoan(), loanTransaction);
|
return processLatestTransactionProgressiveInterestRecalculation(advancedProcessor, loanTransaction.getLoan(), loanTransaction);
|
||||||
}
|
}
|
||||||
return loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, ctx);
|
return loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, ctx);
|
||||||
|
|||||||
+22
-4
@@ -36,8 +36,11 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ProgressiveTransactionCtx;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -145,10 +148,25 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) {
|
public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) {
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loanTransactionProcessingService.processLatestTransaction(
|
LoanRepaymentScheduleTransactionProcessor transactionProcessor = loanTransactionProcessingService
|
||||||
loan.getTransactionProcessingStrategyCode(), loanTransaction,
|
.getTransactionProcessor(loan.getTransactionProcessingStrategyCode());
|
||||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail()));
|
TransactionCtx transactionCtx;
|
||||||
|
if (transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedProcessor) {
|
||||||
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
||||||
|
loanTransaction.getTransactionDate());
|
||||||
|
ProgressiveLoanInterestScheduleModel model = savedModel
|
||||||
|
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
|
||||||
|
|
||||||
|
transactionCtx = new ProgressiveTransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||||
|
loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail(), model);
|
||||||
|
} else {
|
||||||
|
transactionCtx = new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||||
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail());
|
||||||
|
}
|
||||||
|
|
||||||
|
final ChangedTransactionDetail changedTransactionDetail = loanTransactionProcessingService
|
||||||
|
.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction, transactionCtx);
|
||||||
final List<LoanTransaction> newTransactions = changedTransactionDetail.getTransactionChanges().stream()
|
final List<LoanTransaction> newTransactions = changedTransactionDetail.getTransactionChanges().stream()
|
||||||
.map(TransactionChangeData::getNewTransaction).peek(transaction -> transaction.updateLoan(loan)).toList();
|
.map(TransactionChangeData::getNewTransaction).peek(transaction -> transaction.updateLoan(loan)).toList();
|
||||||
loan.getLoanTransactions().addAll(newTransactions);
|
loan.getLoanTransactions().addAll(newTransactions);
|
||||||
|
|||||||
-109
@@ -6000,115 +6000,6 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// uc156: Avoid Loan Reschedule to modify Interest Rate from X value to Zero
|
|
||||||
// 1. Create a Loan product
|
|
||||||
// 2. Submit, Approve and Disburse Loan with Nominal Interest equal to 4%
|
|
||||||
// 3. Apply a Loan repayment
|
|
||||||
// 4. Try to create Loan Reschedule with new Interest Rate equal to zero to get the exception
|
|
||||||
@Test
|
|
||||||
public void uc156() {
|
|
||||||
final String operationDate = "1 April 2025";
|
|
||||||
AtomicLong createdLoanId = new AtomicLong();
|
|
||||||
runAt("1 April 2025", () -> {
|
|
||||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
|
||||||
PostLoanProductsRequest product = create4IProgressive().interestRatePerPeriod(4.0).numberOfRepayments(4)//
|
|
||||||
.installmentAmountInMultiplesOf(null)//
|
|
||||||
.multiDisburseLoan(false)//
|
|
||||||
.disallowExpectedDisbursements(null)//
|
|
||||||
.allowApprovedDisbursedAmountsOverApplied(false)//
|
|
||||||
.overAppliedCalculationType(null)//
|
|
||||||
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)//
|
|
||||||
.overAppliedNumber(null)//
|
|
||||||
;//
|
|
||||||
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
|
||||||
PostLoansRequest applicationRequest = applyLP2ProgressiveLoanRequest(clientId, loanProductResponse.getResourceId(),
|
|
||||||
operationDate, 1000.0, 4.0, 4, null);
|
|
||||||
|
|
||||||
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
|
|
||||||
createdLoanId.set(loanResponse.getLoanId());
|
|
||||||
|
|
||||||
loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest()
|
|
||||||
.approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en"));
|
|
||||||
|
|
||||||
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
|
|
||||||
.dateFormat(DATETIME_PATTERN).locale("en").transactionAmount(BigDecimal.valueOf(1000.0)));
|
|
||||||
});
|
|
||||||
|
|
||||||
runAt("1 May 2025", () -> {
|
|
||||||
executeInlineCOB(createdLoanId.get());
|
|
||||||
|
|
||||||
loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new PostLoansLoanIdTransactionsRequest()
|
|
||||||
.transactionDate("1 May 2025").dateFormat("dd MMMM yyyy").locale("en").transactionAmount(250.00));
|
|
||||||
});
|
|
||||||
|
|
||||||
runAt("6 May 2025", () -> {
|
|
||||||
executeInlineCOB(createdLoanId.get());
|
|
||||||
|
|
||||||
CallFailedRuntimeException callFailedRuntimeException = Assertions.assertThrows(CallFailedRuntimeException.class,
|
|
||||||
() -> loanRescheduleRequestHelper.createLoanRescheduleRequest(new PostCreateRescheduleLoansRequest()
|
|
||||||
.loanId(createdLoanId.get()).dateFormat(DATETIME_PATTERN).locale("en").submittedOnDate("6 May 2025")
|
|
||||||
.newInterestRate(BigDecimal.ZERO).rescheduleReasonId(1L).rescheduleFromDate("1 June 2025")));
|
|
||||||
|
|
||||||
Assertions.assertTrue(
|
|
||||||
callFailedRuntimeException.getMessage().contains("The parameter `newInterestRate` must be greater than 0."));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// uc157: Avoid Loan Reschedule to modify Interest Rate from Zero to X value
|
|
||||||
// 1. Create a Loan product
|
|
||||||
// 2. Submit, Approve and Disburse Loan with Nominal Interest equal to 0 (zero)
|
|
||||||
// 3. Apply a Loan repayment
|
|
||||||
// 4. Try to create Loan Reschedule with new Interest Rate greater than zero to get the exception
|
|
||||||
@Test
|
|
||||||
public void uc157() {
|
|
||||||
final String operationDate = "1 April 2025";
|
|
||||||
AtomicLong createdLoanId = new AtomicLong();
|
|
||||||
runAt("1 April 2025", () -> {
|
|
||||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
|
||||||
PostLoanProductsRequest product = create4IProgressive().interestRatePerPeriod(0.0).numberOfRepayments(4)//
|
|
||||||
.installmentAmountInMultiplesOf(null)//
|
|
||||||
.multiDisburseLoan(false)//
|
|
||||||
.disallowExpectedDisbursements(null)//
|
|
||||||
.allowApprovedDisbursedAmountsOverApplied(false)//
|
|
||||||
.overAppliedCalculationType(null)//
|
|
||||||
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)//
|
|
||||||
.overAppliedNumber(null)//
|
|
||||||
;//
|
|
||||||
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
|
||||||
PostLoansRequest applicationRequest = applyLP2ProgressiveLoanRequest(clientId, loanProductResponse.getResourceId(),
|
|
||||||
operationDate, 1000.0, 0.0, 4, null);
|
|
||||||
|
|
||||||
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
|
|
||||||
createdLoanId.set(loanResponse.getLoanId());
|
|
||||||
|
|
||||||
loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest()
|
|
||||||
.approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en"));
|
|
||||||
|
|
||||||
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
|
|
||||||
.dateFormat(DATETIME_PATTERN).locale("en").transactionAmount(BigDecimal.valueOf(1000.0)));
|
|
||||||
});
|
|
||||||
|
|
||||||
runAt("1 May 2025", () -> {
|
|
||||||
executeInlineCOB(createdLoanId.get());
|
|
||||||
|
|
||||||
loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new PostLoansLoanIdTransactionsRequest()
|
|
||||||
.transactionDate("1 May 2025").dateFormat("dd MMMM yyyy").locale("en").transactionAmount(250.00));
|
|
||||||
});
|
|
||||||
|
|
||||||
runAt("6 May 2025", () -> {
|
|
||||||
executeInlineCOB(createdLoanId.get());
|
|
||||||
|
|
||||||
CallFailedRuntimeException callFailedRuntimeException = Assertions.assertThrows(CallFailedRuntimeException.class,
|
|
||||||
() -> loanRescheduleRequestHelper.createLoanRescheduleRequest(new PostCreateRescheduleLoansRequest()
|
|
||||||
.loanId(createdLoanId.get()).dateFormat(DATETIME_PATTERN).locale("en").submittedOnDate("6 May 2025")
|
|
||||||
.newInterestRate(BigDecimal.valueOf(4.0)).rescheduleReasonId(1L).rescheduleFromDate("1 June 2025")));
|
|
||||||
|
|
||||||
LOG.info("ERROR: {}", callFailedRuntimeException.getMessage());
|
|
||||||
Assertions.assertTrue(
|
|
||||||
callFailedRuntimeException.getMessage().contains("Loan rescheduling is not allowed from interest rate 0 (zero)"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
|
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
|
||||||
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
|
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
|
||||||
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");
|
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");
|
||||||
|
|||||||
Reference in New Issue
Block a user