FINERACT-1981: Progressive Loan schedule handling - Reschedule installment (Extend repayment period)
This commit is contained in:
@@ -5809,8 +5809,7 @@ Feature: Loan
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 February 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
|
||||
# TODO unskip and check when PS-1654 is done
|
||||
@Skip @TestRailId:C3127
|
||||
@TestRailId:C3127
|
||||
Scenario: Verify fixed length loan account Loan schedule - UC9: Reschedule by extra terms a loan account with fixed length of 40 days
|
||||
When Admin sets the business date to "01 February 2024"
|
||||
When Admin creates a client with random data
|
||||
@@ -5843,9 +5842,9 @@ Feature: Loan
|
||||
| 1 | 0 | 01 February 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
||||
| 2 | 15 | 16 February 2024 | | 600.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
|
||||
| 3 | 15 | 02 March 2024 | | 450.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
|
||||
| 4 | 15 | 17 March 2024 | | 300.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
|
||||
| 4 | 10 | 12 March 2024 | | 300.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
|
||||
| 5 | 15 | 01 April 2024 | | 150.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
|
||||
| 6 | 10 | 11 April 2024 | | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
|
||||
| 6 | 15 | 16 April 2024 | | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.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 | 1000.0 | 0.0 | 0.0 | 0 | 1000.0 |
|
||||
@@ -7809,20 +7808,20 @@ Feature: Loan
|
||||
Then Loan's all installments have obligations met
|
||||
|
||||
@TestRailId:C3963
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period: Basic scenario without downpayment
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period: Basic scenario without downpayment, flat interest type
|
||||
When Admin sets the business date to "01 June 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 June 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
| 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 |
|
||||
| LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 June 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1000" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1000" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 666.67 | 333.33 | 0.0 | 0.0 | 0.0 | 333.33 | 0.0 | 0.0 | 0.0 | 333.33 |
|
||||
| 2 | 31 | 01 August 2024 | | 333.34 | 333.33 | 0.0 | 0.0 | 0.0 | 333.33 | 0.0 | 0.0 | 0.0 | 333.33 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 333.34 | 0.0 | 0.0 | 0.0 | 333.34 | 0.0 | 0.0 | 0.0 | 333.34 |
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 667.0 | 333.0 | 0.0 | 0.0 | 0.0 | 333.0 | 0.0 | 0.0 | 0.0 | 333.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 334.0 | 333.0 | 0.0 | 0.0 | 0.0 | 333.0 | 0.0 | 0.0 | 0.0 | 333.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 334.0 | 0.0 | 0.0 | 0.0 | 334.0 | 0.0 | 0.0 | 0.0 | 334.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 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
@@ -7836,10 +7835,10 @@ Feature: Loan
|
||||
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 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
||||
| 4 | 30 | 01 October 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
||||
| 1 | 30 | 01 July 2024 | | 667.0 | 333.0 | 0.0 | 0.0 | 0.0 | 333.0 | 0.0 | 0.0 | 0.0 | 333.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 445.0 | 222.0 | 0.0 | 0.0 | 0.0 | 222.0 | 0.0 | 0.0 | 0.0 | 222.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 223.0 | 222.0 | 0.0 | 0.0 | 0.0 | 222.0 | 0.0 | 0.0 | 0.0 | 222.0 |
|
||||
| 4 | 30 | 01 October 2024 | | 0.0 | 223.0 | 0.0 | 0.0 | 0.0 | 223.0 | 0.0 | 0.0 | 0.0 | 223.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 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
@@ -7848,7 +7847,7 @@ Feature: Loan
|
||||
| 01 June 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
|
||||
@TestRailId:C3964
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period with downpayment installment
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period with downpayment installment, flat interest type
|
||||
When Admin sets the business date to "01 June 2024"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
@@ -7872,7 +7871,7 @@ Feature: Loan
|
||||
When Admin sets the business date to "01 July 2024"
|
||||
When Admin creates and approves Loan reschedule with the following data:
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 August 2024 | 01 July 2024 | | | | 1 | |
|
||||
| 01 July 2024 | 01 July 2024 | | | | 1 | |
|
||||
Then Loan Repayment schedule has 5 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 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
@@ -7889,12 +7888,12 @@ Feature: Loan
|
||||
| 01 June 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
|
||||
@TestRailId:C3965
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period - multiple extra terms
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period - multiple extra terms, flat interest type
|
||||
When Admin sets the business date to "01 June 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 June 2024 | 1500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
| 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 |
|
||||
| LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 June 2024 | 1500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1500" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1500" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
@@ -7912,7 +7911,7 @@ Feature: Loan
|
||||
When Admin sets the business date to "01 July 2024"
|
||||
When Admin creates and approves Loan reschedule with the following data:
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 August 2024 | 01 July 2024 | | | | 2 | |
|
||||
| 01 July 2024 | 01 July 2024 | | | | 2 | |
|
||||
Then Loan Repayment schedule has 5 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 June 2024 | | 1500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
@@ -7929,12 +7928,12 @@ Feature: Loan
|
||||
| 01 June 2024 | Disbursement | 1500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1500.0 |
|
||||
|
||||
@TestRailId:C3966
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period after partial repayment
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period after partial repayment, flat interest type
|
||||
When Admin sets the business date to "01 June 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 June 2024 | 1200 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
| 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 |
|
||||
| LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 June 2024 | 1200 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1200" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1200" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 periods, with the following data for periods:
|
||||
@@ -7969,12 +7968,12 @@ Feature: Loan
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 August 2024 | 15 July 2024 | | | | 1 | |
|
||||
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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | 01 July 2024 | 800.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 533.33 | 266.67 | 0.0 | 0.0 | 0.0 | 266.67 | 0.0 | 0.0 | 0.0 | 266.67 |
|
||||
| 3 | 31 | 01 September 2024 | | 266.66 | 266.67 | 0.0 | 0.0 | 0.0 | 266.67 | 0.0 | 0.0 | 0.0 | 266.67 |
|
||||
| 4 | 30 | 01 October 2024 | | 0.0 | 266.66 | 0.0 | 0.0 | 0.0 | 266.66 | 0.0 | 0.0 | 0.0 | 266.66 |
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | 01 July 2024 | 800.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 533.0 | 267.0 | 0.0 | 0.0 | 0.0 | 267.0 | 0.0 | 0.0 | 0.0 | 267.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 266.0 | 267.0 | 0.0 | 0.0 | 0.0 | 267.0 | 0.0 | 0.0 | 0.0 | 267.0 |
|
||||
| 4 | 30 | 01 October 2024 | | 0.0 | 266.0 | 0.0 | 0.0 | 0.0 | 266.0 | 0.0 | 0.0 | 0.0 | 266.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 0.0 | 0.0 | 0.0 | 1200.0 | 400.0 | 0.0 | 0.0 | 800.0 |
|
||||
@@ -7982,3 +7981,254 @@ Feature: Loan
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
| 01 July 2024 | Repayment | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 800.0 |
|
||||
|
||||
@TestRailId:C3967
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment periods after partial repayment and then backdated repayment occurs, flat interest type
|
||||
When Admin sets the business date to "01 June 2024"
|
||||
When Admin creates a client with random data
|
||||
When Admin set "LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "LAST_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 |
|
||||
| LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 June 2024 | 1200 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1200" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1200" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 800.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 0.0 | 0.0 | 0.0 | 1200.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
When Admin sets the business date to "01 July 2024"
|
||||
And Customer makes "AUTOPAY" repayment on "01 July 2024" with 400 EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | 01 July 2024 | 800.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 0.0 | 0.0 | 0.0 | 1200.0 | 400.0 | 0.0 | 0.0 | 800.0 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
| 01 July 2024 | Repayment | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 800.0 |
|
||||
When Admin sets the business date to "15 July 2024"
|
||||
When Admin creates and approves Loan reschedule with the following data:
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 September 2024 | 15 July 2024 | | | | 2 | |
|
||||
Then Loan Repayment schedule has 5 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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | 01 July 2024 | 800.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 267.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 |
|
||||
| 4 | 30 | 01 October 2024 | | 134.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 |
|
||||
| 5 | 31 | 01 November 2024 | | 0.0 | 134.0 | 0.0 | 0.0 | 0.0 | 134.0 | 0.0 | 0.0 | 0.0 | 134.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 0.0 | 0.0 | 0.0 | 1200.0 | 400.0 | 0.0 | 0.0 | 800.0 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
| 01 July 2024 | Repayment | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 800.0 |
|
||||
When Admin sets the business date to "15 October 2024"
|
||||
And Customer makes "AUTOPAY" repayment on "01 August 2024" with 500 EUR transaction amount
|
||||
Then Loan Repayment schedule has 5 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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | 01 July 2024 | 800.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 31 | 01 August 2024 | 01 August 2024 | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 01 September 2024 | | 267.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 |
|
||||
| 4 | 30 | 01 October 2024 | | 134.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 | 0.0 | 0.0 | 0.0 | 133.0 |
|
||||
| 5 | 31 | 01 November 2024 | | 0.0 | 134.0 | 0.0 | 0.0 | 0.0 | 134.0 | 100.0 | 100.0 | 0.0 | 34.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 0.0 | 0.0 | 0.0 | 1200.0 | 900.0 | 100.0 | 0.0 | 300.0 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
| 01 July 2024 | Repayment | 400.0 | 400.0 | 0.0 | 0.0 | 0.0 | 800.0 |
|
||||
| 01 August 2024 | Repayment | 500.0 | 500.0 | 0.0 | 0.0 | 0.0 | 300.0 |
|
||||
When Admin set "LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
|
||||
|
||||
@TestRailId:C3987
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period: Basic scenario without downpayment, declining balance interest type
|
||||
When Admin sets the business date to "01 June 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 June 2024 | 1000 | 12 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1000" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1000" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 669.98 | 330.02 | 10.0 | 0.0 | 0.0 | 340.02 | 0.0 | 0.0 | 0.0 | 340.02 |
|
||||
| 2 | 31 | 01 August 2024 | | 336.66 | 333.32 | 6.7 | 0.0 | 0.0 | 340.02 | 0.0 | 0.0 | 0.0 | 340.02 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 336.66 | 3.37 | 0.0 | 0.0 | 340.03 | 0.0 | 0.0 | 0.0 | 340.03 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1000.0 | 20.07 | 0.0 | 0.0 | 1020.07 | 0.0 | 0.0 | 0.0 | 1020.07 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
When Admin sets the business date to "01 July 2024"
|
||||
When Admin creates and approves Loan reschedule with the following data:
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 August 2024 | 01 July 2024 | | | | 1 | |
|
||||
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 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 669.98 | 330.02 | 10.0 | 0.0 | 0.0 | 340.02 | 0.0 | 0.0 | 0.0 | 340.02 |
|
||||
| 2 | 31 | 01 August 2024 | | 448.87 | 221.11 | 6.7 | 0.0 | 0.0 | 227.81 | 0.0 | 0.0 | 0.0 | 227.81 |
|
||||
| 3 | 31 | 01 September 2024 | | 225.55 | 223.32 | 4.49 | 0.0 | 0.0 | 227.81 | 0.0 | 0.0 | 0.0 | 227.81 |
|
||||
| 4 | 30 | 01 October 2024 | | 0.0 | 225.55 | 2.26 | 0.0 | 0.0 | 227.81 | 0.0 | 0.0 | 0.0 | 227.81 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1000.0 | 23.45 | 0.0 | 0.0 | 1023.45 | 0.0 | 0.0 | 0.0 | 1023.45 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
|
||||
@TestRailId:C3989
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period with downpayment installment, declining balance interest type
|
||||
When Admin sets the business date to "01 June 2024"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_DOWNPAYMENT_INTEREST | 01 June 2024 | 1000 | 12 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1000" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1000" EUR transaction amount
|
||||
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 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 0 | 01 June 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
||||
| 2 | 30 | 01 July 2024 | | 502.4 | 247.6 | 7.4 | 0.0 | 0.0 | 255.0 | 0.0 | 0.0 | 0.0 | 255.0 |
|
||||
| 3 | 31 | 01 August 2024 | | 252.52 | 249.88 | 5.12 | 0.0 | 0.0 | 255.0 | 0.0 | 0.0 | 0.0 | 255.0 |
|
||||
| 4 | 31 | 01 September 2024 | | 0.0 | 252.52 | 2.57 | 0.0 | 0.0 | 255.09 | 0.0 | 0.0 | 0.0 | 255.09 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1000.0 | 15.09 | 0.0 | 0.0 | 1015.09 | 0.0 | 0.0 | 0.0 | 1015.09 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
When Admin sets the business date to "01 July 2024"
|
||||
When Admin creates and approves Loan reschedule with the following data:
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 July 2024 | 01 July 2024 | | | | 1 | |
|
||||
Then Loan Repayment schedule has 5 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 June 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 0 | 01 June 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
|
||||
| 2 | 30 | 01 July 2024 | | 629.4 | 120.6 | 7.4 | 0.0 | 0.0 | 128.0 | 0.0 | 0.0 | 0.0 | 128.0 |
|
||||
| 3 | 31 | 01 August 2024 | | 507.81 | 121.59 | 6.41 | 0.0 | 0.0 | 128.0 | 0.0 | 0.0 | 0.0 | 128.0 |
|
||||
| 4 | 31 | 01 September 2024 | | 384.99 | 122.82 | 5.18 | 0.0 | 0.0 | 128.0 | 0.0 | 0.0 | 0.0 | 128.0 |
|
||||
| 5 | 30 | 01 October 2024 | | 0.0 | 384.99 | 3.8 | 0.0 | 0.0 | 388.79 | 0.0 | 0.0 | 0.0 | 388.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 | 22.79 | 0.0 | 0.0 | 1022.79 | 0.0 | 0.0 | 0.0 | 1022.79 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
|
||||
|
||||
@TestRailId:C3990
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period - multiple extra terms, declining balance interest type
|
||||
When Admin sets the business date to "01 June 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 June 2024 | 1500 | 12 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1500" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1500" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 June 2024 | | 1500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 1004.97 | 495.03 | 15.0 | 0.0 | 0.0 | 510.03 | 0.0 | 0.0 | 0.0 | 510.03 |
|
||||
| 2 | 31 | 01 August 2024 | | 504.99 | 499.98 | 10.05 | 0.0 | 0.0 | 510.03 | 0.0 | 0.0 | 0.0 | 510.03 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 504.99 | 5.05 | 0.0 | 0.0 | 510.04 | 0.0 | 0.0 | 0.0 | 510.04 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1500.0 | 30.1 | 0.0 | 0.0 | 1530.1 | 0.0 | 0.0 | 0.0 | 1530.1 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1500.0 |
|
||||
When Admin sets the business date to "01 July 2024"
|
||||
When Admin creates and approves Loan reschedule with the following data:
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 July 2024 | 01 July 2024 | | | | 2 | |
|
||||
Then Loan Repayment schedule has 5 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 June 2024 | | 1500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 1205.94 | 294.06 | 15.0 | 0.0 | 0.0 | 309.06 | 0.0 | 0.0 | 0.0 | 309.06 |
|
||||
| 2 | 31 | 01 August 2024 | | 908.94 | 297.0 | 12.06 | 0.0 | 0.0 | 309.06 | 0.0 | 0.0 | 0.0 | 309.06 |
|
||||
| 3 | 31 | 01 September 2024 | | 608.97 | 299.97 | 9.09 | 0.0 | 0.0 | 309.06 | 0.0 | 0.0 | 0.0 | 309.06 |
|
||||
| 4 | 30 | 01 October 2024 | | 306.0 | 302.97 | 6.09 | 0.0 | 0.0 | 309.06 | 0.0 | 0.0 | 0.0 | 309.06 |
|
||||
| 5 | 31 | 01 November 2024 | | 0.0 | 306.0 | 3.06 | 0.0 | 0.0 | 309.06 | 0.0 | 0.0 | 0.0 | 309.06 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1500.0 | 45.3 | 0.0 | 0.0 | 1545.3 | 0.0 | 0.0 | 0.0 | 1545.3 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1500.0 |
|
||||
|
||||
@TestRailId:C3994
|
||||
Scenario: Verify Progressive Loan reschedule by extending repayment period after partial repayment, declining balance interest type
|
||||
When Admin sets the business date to "01 June 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 June 2024 | 1200 | 12 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "01 June 2024" with "1200" amount and expected disbursement date on "01 June 2024"
|
||||
When Admin successfully disburse the loan on "01 June 2024" with "1200" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | | 803.97 | 396.03 | 12.0 | 0.0 | 0.0 | 408.03 | 0.0 | 0.0 | 0.0 | 408.03 |
|
||||
| 2 | 31 | 01 August 2024 | | 403.98 | 399.99 | 8.04 | 0.0 | 0.0 | 408.03 | 0.0 | 0.0 | 0.0 | 408.03 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 403.98 | 4.04 | 0.0 | 0.0 | 408.02 | 0.0 | 0.0 | 0.0 | 408.02 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 24.08 | 0.0 | 0.0 | 1224.08 | 0.0 | 0.0 | 0.0 | 1224.08 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
When Admin sets the business date to "01 July 2024"
|
||||
And Customer makes "AUTOPAY" repayment on "01 July 2024" with 417.03 EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | 01 July 2024 | 803.97 | 396.03 | 12.0 | 0.0 | 0.0 | 408.03 | 408.03 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 403.89 | 400.08 | 7.95 | 0.0 | 0.0 | 408.03 | 9.0 | 9.0 | 0.0 | 399.03 |
|
||||
| 3 | 31 | 01 September 2024 | | 0.0 | 403.89 | 4.04 | 0.0 | 0.0 | 407.93 | 0.0 | 0.0 | 0.0 | 407.93 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 23.99 | 0.0 | 0.0 | 1223.99 | 417.03 | 9.0 | 0.0 | 806.96 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
| 01 July 2024 | Repayment | 417.03 | 405.03 | 12.0 | 0.0 | 0.0 | 794.97 |
|
||||
When Admin sets the business date to "15 July 2024"
|
||||
When Admin creates and approves Loan reschedule with the following data:
|
||||
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
|
||||
| 01 August 2024 | 15 July 2024 | | | | 1 | |
|
||||
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 June 2024 | | 1200.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 30 | 01 July 2024 | 01 July 2024 | 803.97 | 396.03 | 12.0 | 0.0 | 0.0 | 408.03 | 408.03 | 0.0 | 0.0 | 0.0 |
|
||||
| 2 | 31 | 01 August 2024 | | 538.58 | 265.39 | 7.95 | 0.0 | 0.0 | 273.34 | 9.0 | 9.0 | 0.0 | 264.34 |
|
||||
| 3 | 31 | 01 September 2024 | | 270.63 | 267.95 | 5.39 | 0.0 | 0.0 | 273.34 | 0.0 | 0.0 | 0.0 | 273.34 |
|
||||
| 4 | 30 | 01 October 2024 | | 0.0 | 270.63 | 2.71 | 0.0 | 0.0 | 273.34 | 0.0 | 0.0 | 0.0 | 273.34 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1200.0 | 28.05 | 0.0 | 0.0 | 1228.05 | 417.03 | 9.0 | 0.0 | 811.02 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 01 June 2024 | Disbursement | 1200.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1200.0 |
|
||||
| 01 July 2024 | Repayment | 417.03 | 405.03 | 12.0 | 0.0 | 0.0 | 794.97 |
|
||||
|
||||
+60
-20
@@ -53,6 +53,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -234,9 +235,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
|
||||
List<LoanTransaction> overpaidTransactions = new ArrayList<>();
|
||||
for (final ChangeOperation changeOperation : changeOperations) {
|
||||
if (changeOperation.isInterestRateChange()) {
|
||||
final LoanTermVariationsData interestRateChange = changeOperation.getInterestRateChange().get();
|
||||
processInterestRateChange(installments, interestRateChange, scheduleModel);
|
||||
if (changeOperation.isLoanTermVariationsData()) {
|
||||
final LoanTermVariationsData interestRateChange = changeOperation.getLoanTermVariationsData().get();
|
||||
processLoanTermVariation(installments, interestRateChange, scheduleModel);
|
||||
} else if (changeOperation.isTransaction()) {
|
||||
LoanTransaction transaction = changeOperation.getLoanTransaction().get();
|
||||
processSingleTransaction(transaction, ctx);
|
||||
@@ -301,16 +302,55 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.map(TransactionChangeData::getNewTransaction).findFirst().orElse(transaction);
|
||||
}
|
||||
|
||||
private void processInterestRateChange(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTermVariationsData interestRateChange, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
final LocalDate interestRateChangeSubmittedOnDate = interestRateChange.getTermVariationApplicableFrom();
|
||||
final BigDecimal newInterestRate = interestRateChange.getDecimalValue();
|
||||
if (interestRateChange.getTermVariationType().isInterestPauseVariation()) {
|
||||
final LocalDate pauseEndDate = interestRateChange.getDateValue();
|
||||
emiCalculator.applyInterestPause(scheduleModel, interestRateChangeSubmittedOnDate, pauseEndDate);
|
||||
} else {
|
||||
emiCalculator.changeInterestRate(scheduleModel, interestRateChangeSubmittedOnDate, newInterestRate);
|
||||
private void processLoanTermVariation(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTermVariationsData termVariationsData, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
switch (termVariationsData.getTermVariationType()) {
|
||||
case INTEREST_PAUSE -> handleInterestPause(installments, termVariationsData, scheduleModel);
|
||||
case INTEREST_RATE_FROM_INSTALLMENT -> handleChangeInterestRate(installments, termVariationsData, scheduleModel);
|
||||
case EXTEND_REPAYMENT_PERIOD -> handleExtraRepaymentPeriod(installments, termVariationsData, scheduleModel);
|
||||
default -> throw new IllegalStateException("Unhandled LoanTermVariationType.");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleExtraRepaymentPeriod(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTermVariationsData termVariationsData, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
final LocalDate interestRateChangeSubmittedOnDate = termVariationsData.getTermVariationApplicableFrom();
|
||||
final int repaymentPeriodsToAdd = termVariationsData.getDecimalValue().intValue();
|
||||
emiCalculator.addRepaymentPeriods(scheduleModel, interestRateChangeSubmittedOnDate, repaymentPeriodsToAdd);
|
||||
final Loan loan = installments.getFirst().getLoan();
|
||||
|
||||
int nextInstallmentNumber = installments.stream().mapToInt(LoanRepaymentScheduleInstallment::getInstallmentNumber).max().orElse(0)
|
||||
+ 1;
|
||||
|
||||
for (int i = 0; i < scheduleModel.repaymentPeriods().size(); i++) {
|
||||
final RepaymentPeriod rp = scheduleModel.repaymentPeriods().get(i);
|
||||
// Check if this period already exists in installments
|
||||
if (installments.stream().noneMatch(installment -> installment.getFromDate().equals(rp.getFromDate())
|
||||
&& installment.getDueDate().equals(rp.getDueDate()))) {
|
||||
|
||||
final LoanRepaymentScheduleInstallment newInstallment = new LoanRepaymentScheduleInstallment(loan, nextInstallmentNumber,
|
||||
rp.getFromDate(), rp.getDueDate(), rp.getDuePrincipal().getAmount(), rp.getDueInterest().getAmount(), ZERO, ZERO,
|
||||
false, null, ZERO);
|
||||
installments.add(newInstallment);
|
||||
nextInstallmentNumber++;
|
||||
}
|
||||
}
|
||||
processInterestRateChangeOnInstallments(scheduleModel, interestRateChangeSubmittedOnDate, installments);
|
||||
}
|
||||
|
||||
private void handleChangeInterestRate(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTermVariationsData termVariationsData, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
final LocalDate interestRateChangeSubmittedOnDate = termVariationsData.getTermVariationApplicableFrom();
|
||||
final BigDecimal newInterestRate = termVariationsData.getDecimalValue();
|
||||
emiCalculator.changeInterestRate(scheduleModel, interestRateChangeSubmittedOnDate, newInterestRate);
|
||||
processInterestRateChangeOnInstallments(scheduleModel, interestRateChangeSubmittedOnDate, installments);
|
||||
}
|
||||
|
||||
private void handleInterestPause(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTermVariationsData termVariationsData, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
final LocalDate interestRateChangeSubmittedOnDate = termVariationsData.getTermVariationApplicableFrom();
|
||||
final LocalDate pauseEndDate = termVariationsData.getDateValue();
|
||||
emiCalculator.applyInterestPause(scheduleModel, interestRateChangeSubmittedOnDate, pauseEndDate);
|
||||
processInterestRateChangeOnInstallments(scheduleModel, interestRateChangeSubmittedOnDate, installments);
|
||||
}
|
||||
|
||||
@@ -1204,14 +1244,14 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
List<ChangeOperation> changeOperations = new ArrayList<>();
|
||||
Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariationsMap = loanTermVariations.stream()
|
||||
.collect(Collectors.groupingBy(ltvd -> LoanTermVariationType.fromInt(ltvd.getTermType().getId().intValue())));
|
||||
if (loanTermVariationsMap.get(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT) != null) {
|
||||
changeOperations.addAll(loanTermVariationsMap.get(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT).stream()
|
||||
.map(ChangeOperation::new).toList());
|
||||
}
|
||||
if (loanTermVariationsMap.get(LoanTermVariationType.INTEREST_PAUSE) != null) {
|
||||
changeOperations
|
||||
.addAll(loanTermVariationsMap.get(LoanTermVariationType.INTEREST_PAUSE).stream().map(ChangeOperation::new).toList());
|
||||
}
|
||||
|
||||
Stream.of(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT, LoanTermVariationType.INTEREST_PAUSE,
|
||||
LoanTermVariationType.EXTEND_REPAYMENT_PERIOD).forEach(key -> {
|
||||
if (loanTermVariationsMap.get(key) != null) {
|
||||
changeOperations.addAll(loanTermVariationsMap.get(key).stream().map(ChangeOperation::new).toList());
|
||||
}
|
||||
});
|
||||
|
||||
if (charges != null) {
|
||||
changeOperations.addAll(charges.stream().map(ChangeOperation::new).toList());
|
||||
}
|
||||
|
||||
+11
-11
@@ -32,30 +32,30 @@ import org.springframework.lang.NonNull;
|
||||
@Getter
|
||||
public class ChangeOperation implements Comparable<ChangeOperation> {
|
||||
|
||||
private final Optional<LoanTermVariationsData> interestRateChange;
|
||||
private final Optional<LoanTermVariationsData> loanTermVariationsData;
|
||||
private final Optional<LoanCharge> loanCharge;
|
||||
private final Optional<LoanTransaction> loanTransaction;
|
||||
|
||||
public ChangeOperation(LoanCharge loanCharge) {
|
||||
this.interestRateChange = Optional.empty();
|
||||
this.loanTermVariationsData = Optional.empty();
|
||||
this.loanCharge = Optional.of(loanCharge);
|
||||
this.loanTransaction = Optional.empty();
|
||||
}
|
||||
|
||||
public ChangeOperation(LoanTransaction loanTransaction) {
|
||||
this.interestRateChange = Optional.empty();
|
||||
this.loanTermVariationsData = Optional.empty();
|
||||
this.loanTransaction = Optional.of(loanTransaction);
|
||||
this.loanCharge = Optional.empty();
|
||||
}
|
||||
|
||||
public ChangeOperation(LoanTermVariationsData interestRateChange) {
|
||||
this.interestRateChange = Optional.of(interestRateChange);
|
||||
public ChangeOperation(LoanTermVariationsData loanTermVariationsData) {
|
||||
this.loanTermVariationsData = Optional.of(loanTermVariationsData);
|
||||
this.loanTransaction = Optional.empty();
|
||||
this.loanCharge = Optional.empty();
|
||||
}
|
||||
|
||||
public boolean isInterestRateChange() {
|
||||
return interestRateChange.isPresent();
|
||||
public boolean isLoanTermVariationsData() {
|
||||
return loanTermVariationsData.isPresent();
|
||||
}
|
||||
|
||||
public boolean isTransaction() {
|
||||
@@ -80,7 +80,7 @@ public class ChangeOperation implements Comparable<ChangeOperation> {
|
||||
}
|
||||
|
||||
private LocalDate getEffectiveDate() {
|
||||
if (interestRateChange.isPresent()) {
|
||||
if (loanTermVariationsData.isPresent()) {
|
||||
return getSubmittedOnDate();
|
||||
} else if (loanCharge.isPresent()) {
|
||||
if (isBackdatedCharge()) {
|
||||
@@ -98,8 +98,8 @@ public class ChangeOperation implements Comparable<ChangeOperation> {
|
||||
}
|
||||
|
||||
private LocalDate getSubmittedOnDate() {
|
||||
if (interestRateChange.isPresent()) {
|
||||
return interestRateChange.get().getTermVariationApplicableFrom();
|
||||
if (loanTermVariationsData.isPresent()) {
|
||||
return loanTermVariationsData.get().getTermVariationApplicableFrom();
|
||||
} else if (loanCharge.isPresent()) {
|
||||
return loanCharge.get().getSubmittedOnDate();
|
||||
} else if (loanTransaction.isPresent()) {
|
||||
@@ -110,7 +110,7 @@ public class ChangeOperation implements Comparable<ChangeOperation> {
|
||||
}
|
||||
|
||||
private OffsetDateTime getCreatedDateTime() {
|
||||
if (interestRateChange.isPresent()) {
|
||||
if (loanTermVariationsData.isPresent()) {
|
||||
return DateUtils.getOffsetDateTimeOfTenantFromLocalDate(getSubmittedOnDate());
|
||||
} else if (loanCharge.isPresent() && loanCharge.get().getCreatedDate().isPresent()) {
|
||||
return loanCharge.get().getCreatedDate().get();
|
||||
|
||||
+60
-23
@@ -38,6 +38,7 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
@@ -78,42 +79,73 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
validateRescheduleReasonId(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
||||
validateRescheduleReasonComment(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
||||
LocalDate adjustedDueDate = validateAndRetrieveAdjustedDate(fromJsonHelper, jsonElement, rescheduleFromDate, dataValidatorBuilder);
|
||||
BigDecimal interestRate = validateInterestRate(fromJsonHelper, jsonElement, dataValidatorBuilder, loan);
|
||||
BigDecimal interestRate = validateInterestRateParam(fromJsonHelper, jsonElement, dataValidatorBuilder, loan);
|
||||
Integer extraTerms = validateExtraTermsParam(fromJsonHelper, jsonElement, dataValidatorBuilder, loan);
|
||||
validateUnsupportedParams(jsonElement, dataValidatorBuilder);
|
||||
|
||||
boolean hasInterestRateChange = interestRate != null;
|
||||
boolean hasAdjustDueDateChange = adjustedDueDate != null;
|
||||
if (hasInterestRateChange) {
|
||||
validateLoanStatusIsActiveOrClosed(loan, dataValidatorBuilder);
|
||||
} else {
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
}
|
||||
boolean hasExtraTermsChange = extraTerms != null;
|
||||
|
||||
if (hasInterestRateChange && hasAdjustDueDateChange) {
|
||||
if (Stream.of(hasInterestRateChange, hasAdjustDueDateChange, hasExtraTermsChange).filter(f -> f).count() > 1) {
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.adjustedDueDateParamName).failWithCode(
|
||||
RescheduleLoansApiConstants.rescheduleMultipleOperationsNotSupportedErrorCode,
|
||||
"Only one operation is supported at a time during Loan Rescheduling");
|
||||
}
|
||||
|
||||
if (rescheduleFromDate != null && hasInterestRateChange) {
|
||||
validateInterestRateChangeRescheduleFromDate(loan, rescheduleFromDate);
|
||||
if (hasExtraTermsChange) {
|
||||
validateExtraTerms(dataValidatorBuilder, loan);
|
||||
} else if (hasAdjustDueDateChange) {
|
||||
validateAdjustDueDateChange(dataValidatorBuilder, loan, rescheduleFromDate);
|
||||
} else if (hasInterestRateChange) {
|
||||
validateInterestRate(dataValidatorBuilder, loan, rescheduleFromDate);
|
||||
}
|
||||
|
||||
LoanRepaymentScheduleInstallment installment;
|
||||
if (hasInterestRateChange) {
|
||||
installment = loan.getRelatedRepaymentScheduleInstallment(rescheduleFromDate);
|
||||
} else {
|
||||
installment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(rescheduleFromDate);
|
||||
}
|
||||
|
||||
validateReschedulingInstallment(dataValidatorBuilder, installment);
|
||||
validateForOverdueCharges(dataValidatorBuilder, loan, installment);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateAdjustDueDateChange(DataValidatorBuilder dataValidatorBuilder, Loan loan, LocalDate rescheduleFromDate) {
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
LoanRepaymentScheduleInstallment installment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(rescheduleFromDate);
|
||||
validateReschedulingInstallment(dataValidatorBuilder, installment);
|
||||
validateForOverdueCharges(dataValidatorBuilder, loan, installment);
|
||||
}
|
||||
|
||||
private void validateInterestRate(DataValidatorBuilder dataValidatorBuilder, Loan loan, LocalDate rescheduleFromDate) {
|
||||
validateLoanStatusIsActiveOrClosed(loan, dataValidatorBuilder);
|
||||
if (rescheduleFromDate != null) {
|
||||
validateInterestRateChangeRescheduleFromDate(loan, rescheduleFromDate);
|
||||
}
|
||||
LoanRepaymentScheduleInstallment installment;
|
||||
installment = loan.getRelatedRepaymentScheduleInstallment(rescheduleFromDate);
|
||||
validateReschedulingInstallment(dataValidatorBuilder, installment);
|
||||
validateForOverdueCharges(dataValidatorBuilder, loan, installment);
|
||||
}
|
||||
|
||||
private void validateExtraTerms(DataValidatorBuilder dataValidatorBuilder, Loan loan) {
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
}
|
||||
|
||||
private Integer validateExtraTermsParam(FromJsonHelper fromJsonHelper, JsonElement jsonElement,
|
||||
DataValidatorBuilder dataValidatorBuilder, Loan loan) {
|
||||
|
||||
final Integer extraTerms = fromJsonHelper.extractIntegerWithLocaleNamed(RescheduleLoansApiConstants.extraTermsParamName,
|
||||
jsonElement);
|
||||
DataValidatorBuilder extraTermsDataValidator = dataValidatorBuilder.reset()
|
||||
.parameter(RescheduleLoansApiConstants.extraTermsParamName).value(extraTerms).ignoreIfNull().integerGreaterThanZero();
|
||||
if (extraTerms != null) {
|
||||
Integer maxNumberOfRepayments = loan.getLoanProduct().getMaxNumberOfRepayments();
|
||||
if (maxNumberOfRepayments != null) {
|
||||
Integer numberOfRepayments = loan.getLoanProductRelatedDetail().getNumberOfRepayments();
|
||||
extraTermsDataValidator.notGreaterThanMax(maxNumberOfRepayments - numberOfRepayments);
|
||||
}
|
||||
}
|
||||
|
||||
return extraTerms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateReschedulingInstallment(DataValidatorBuilder dataValidatorBuilder, LoanRepaymentScheduleInstallment installment) {
|
||||
if (installment == null) {
|
||||
@@ -125,7 +157,6 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
private void validateUnsupportedParams(JsonElement jsonElement, DataValidatorBuilder dataValidatorBuilder) {
|
||||
final var unsupportedFields = List.of(RescheduleLoansApiConstants.graceOnPrincipalParamName, //
|
||||
RescheduleLoansApiConstants.graceOnInterestParamName, //
|
||||
RescheduleLoansApiConstants.extraTermsParamName, //
|
||||
RescheduleLoansApiConstants.emiParamName//
|
||||
);
|
||||
|
||||
@@ -152,6 +183,7 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
LoanRepaymentScheduleInstallment installment;
|
||||
|
||||
boolean hasExtraTerms = false;
|
||||
boolean hasInterestRateChange = false;
|
||||
for (LoanRescheduleRequestToTermVariationMapping mapping : loanRescheduleRequest
|
||||
.getLoanRescheduleRequestToTermVariationMappings()) {
|
||||
@@ -159,6 +191,9 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
if (termType.isInterestRateVariation() || termType.isInterestRateFromInstallment()) {
|
||||
hasInterestRateChange = true;
|
||||
}
|
||||
if (termType.isExtendRepaymentPeriod()) {
|
||||
hasExtraTerms = true;
|
||||
}
|
||||
}
|
||||
if (hasInterestRateChange) {
|
||||
validateLoanStatusIsActiveOrClosed(loan, dataValidatorBuilder);
|
||||
@@ -166,13 +201,15 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
}
|
||||
|
||||
if (loanRescheduleRequest.getInterestRateFromInstallmentTermVariationIfExists() != null) {
|
||||
if (loanRescheduleRequest.getInterestRateFromInstallmentTermVariationIfExists() != null || hasExtraTerms) {
|
||||
installment = loan.getRelatedRepaymentScheduleInstallment(rescheduleFromDate);
|
||||
} else {
|
||||
installment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(rescheduleFromDate);
|
||||
}
|
||||
validateReschedulingInstallment(dataValidatorBuilder, installment);
|
||||
validateForOverdueCharges(dataValidatorBuilder, loan, installment);
|
||||
if (!hasExtraTerms) {
|
||||
validateForOverdueCharges(dataValidatorBuilder, loan, installment);
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
@@ -198,7 +235,7 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal validateInterestRate(final FromJsonHelper fromJsonHelper, final JsonElement jsonElement,
|
||||
private BigDecimal validateInterestRateParam(final FromJsonHelper fromJsonHelper, final JsonElement jsonElement,
|
||||
DataValidatorBuilder dataValidatorBuilder, Loan loan) {
|
||||
final BigDecimal interestRate = fromJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(RescheduleLoansApiConstants.newInterestRateParamName, jsonElement);
|
||||
|
||||
+3
@@ -78,6 +78,9 @@ public interface EMICalculator {
|
||||
void changeInterestRate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate newInterestSubmittedOnDate,
|
||||
BigDecimal newInterestRate);
|
||||
|
||||
void addRepaymentPeriods(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate submittedOnDate,
|
||||
int numberOfRepaymentPeriodsToAdd);
|
||||
|
||||
/**
|
||||
* This method applies outstanding balance correction on the interest model. Negative amount decreases the
|
||||
* outstanding balance while positive amounts are increasing that. Typically used for late repayment or to count
|
||||
|
||||
+87
-4
@@ -26,13 +26,16 @@ import java.time.LocalDate;
|
||||
import java.time.Year;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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;
|
||||
@@ -178,6 +181,58 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
changeInterestRate(scheduleModel, EmiChangeOperation.changeInterestRate(newInterestSubmittedOnDate, newInterestRate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRepaymentPeriods(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate submittedOnDate,
|
||||
final int numberOfRepaymentPeriodsToAdd) {
|
||||
addRepaymentPeriods(scheduleModel,
|
||||
EmiChangeOperation.addRepaymentPeriods(submittedOnDate, scheduleModel.zero(), numberOfRepaymentPeriodsToAdd));
|
||||
}
|
||||
|
||||
public void addRepaymentPeriods(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
||||
LocalDate disbursementDate = scheduleModel.getStartDate();
|
||||
int repaymentPeriodCount = scheduleModel.repaymentPeriods().size();
|
||||
final LocalDate interestRateChangeEffectiveDate = operation.getSubmittedOnDate().minusDays(1);
|
||||
|
||||
List<LocalDateInterval> periods2 = generateAdditionalRepaymentPeriodDueDates(scheduleModel, operation.getPeriodsToAdd(),
|
||||
repaymentPeriodCount, scheduleModel.resolveRepaymentPEriodLengthGeneratorFunction(disbursementDate));
|
||||
updateModel(scheduleModel, periods2, LocalDateInterval::startDate, LocalDateInterval::endDate);
|
||||
|
||||
scheduleModel
|
||||
.changeOutstandingBalanceAndUpdateInterestPeriods(interestRateChangeEffectiveDate, scheduleModel.zero(),
|
||||
scheduleModel.zero(), scheduleModel.zero())
|
||||
.ifPresent(repaymentPeriod -> calculateEMIValueAndRateFactors(
|
||||
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, interestRateChangeEffectiveDate), scheduleModel,
|
||||
operation));
|
||||
}
|
||||
|
||||
public <T> void updateModel(ProgressiveLoanInterestScheduleModel scheduleModel, List<T> updateExpectedRepaymentPeriods,
|
||||
Function<T, LocalDate> from, Function<T, LocalDate> to) {
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
|
||||
MathContext mc = scheduleModel.mc();
|
||||
final Money zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
final AtomicReference<RepaymentPeriod> prev = new AtomicReference<>();
|
||||
RepaymentPeriod originalLAstRepaymentPeriod = scheduleModel.getLastRepaymentPeriod();
|
||||
prev.set(originalLAstRepaymentPeriod);
|
||||
List<RepaymentPeriod> repaymentPeriods = updateExpectedRepaymentPeriods.stream().map(e -> {
|
||||
RepaymentPeriod rp = RepaymentPeriod.create(prev.get(), from.apply(e), to.apply(e), zero, mc, loanProductRelatedDetail);
|
||||
rp.setTotalDisbursedAmount(originalLAstRepaymentPeriod.getTotalDisbursedAmount());
|
||||
rp.setTotalCapitalizedIncomeAmount(originalLAstRepaymentPeriod.getTotalCapitalizedIncomeAmount());
|
||||
prev.set(rp);
|
||||
return rp;
|
||||
}).toList();
|
||||
scheduleModel.repaymentPeriods().addAll(repaymentPeriods);
|
||||
}
|
||||
|
||||
List<LocalDateInterval> generateAdditionalRepaymentPeriodDueDates(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final int periods, final int existingRepayments, Function<Long, LocalDate> repaymentPeriodLengthResolver) {
|
||||
final List<LocalDateInterval> expectedRepaymentPeriods = new ArrayList<>();
|
||||
Integer repayEvery = scheduleModel.loanProductRelatedDetail().getRepayEvery();
|
||||
IntStream.range(existingRepayments, existingRepayments + periods).forEach(
|
||||
i -> expectedRepaymentPeriods.add(LocalDateInterval.create(repaymentPeriodLengthResolver.apply((long) i * repayEvery),
|
||||
repaymentPeriodLengthResolver.apply((long) (i + 1) * repayEvery))));
|
||||
return expectedRepaymentPeriods;
|
||||
}
|
||||
|
||||
private void changeInterestRate(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
||||
final LocalDate interestRateChangeEffectiveDate = operation.getSubmittedOnDate().minusDays(1);
|
||||
scheduleModel.addInterestRate(interestRateChangeEffectiveDate, operation.getInterestRate());
|
||||
@@ -486,7 +541,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
|
||||
final List<RepaymentPeriod> relatedRepaymentPeriods = scheduleModel.getRelatedRepaymentPeriods(calculateFromRepaymentPeriodDueDate);
|
||||
final boolean onlyOnActualModelShouldApply = scheduleModel.isEmpty()
|
||||
|| operation.getAction() == EmiChangeOperation.Action.INTEREST_RATE_CHANGE || scheduleModel.isCopy();
|
||||
|| operation.getAction() == EmiChangeOperation.Action.INTEREST_RATE_CHANGE
|
||||
|| operation.getAction() == EmiChangeOperation.Action.ADD_REPAYMENT_PERIODS || scheduleModel.isCopy();
|
||||
|
||||
calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
|
||||
calculateOutstandingBalance(scheduleModel);
|
||||
@@ -932,13 +988,40 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
Money periodEmi = total.dividedBy(repaymentPeriods.size(), mc);
|
||||
Money periodEmiInMultiplesOf = applyInstallmentAmountInMultiplesOf(scheduleModel, periodEmi);
|
||||
Money remainder = total.minus(periodEmiInMultiplesOf.multipliedBy(repaymentPeriods.size(), mc));
|
||||
AtomicReference<Money> reallocationAmount = new AtomicReference<>(Money.zero(currency));
|
||||
|
||||
repaymentPeriods.forEach(rp -> {
|
||||
Money emi = rp.equals(lastRepaymentPeriod) ? periodEmiInMultiplesOf.add(remainder) : periodEmiInMultiplesOf;
|
||||
rp.setEmi(emi);
|
||||
rp.setOriginalEmi(emi);
|
||||
rp.getInterestPeriods().forEach(InterestPeriod::updateOutstandingLoanBalance);
|
||||
|
||||
if (emi.plus(rp.getTotalCreditedAmount(), mc).plus(rp.getFutureUnrecognizedInterest(), mc)
|
||||
.isGreaterThanOrEqualTo(rp.getTotalPaidAmount())) {
|
||||
rp.setEmi(emi);
|
||||
rp.setOriginalEmi(emi);
|
||||
rp.getInterestPeriods().forEach(InterestPeriod::updateOutstandingLoanBalance);
|
||||
} else {
|
||||
Money adjustment = rp.getTotalPaidAmount()
|
||||
.minus(emi.plus(rp.getTotalCreditedAmount(), mc).plus(rp.getFutureUnrecognizedInterest(), mc));
|
||||
reallocationAmount.set(reallocationAmount.get().add(adjustment));
|
||||
rp.setEmi(rp.getTotalPaidAmount());
|
||||
rp.setOriginalEmi(rp.getTotalPaidAmount());
|
||||
rp.getInterestPeriods().forEach(InterestPeriod::updateOutstandingLoanBalance);
|
||||
}
|
||||
});
|
||||
|
||||
if (reallocationAmount.get().isGreaterThanZero()) {
|
||||
repaymentPeriods.reversed().forEach(rp -> {
|
||||
if (reallocationAmount.get().isGreaterThanZero() && !rp.isFullyPaid()) {
|
||||
Money minimumNewEmi = MathUtil.max(rp.getEmi().minus(reallocationAmount.get()), Money.zero(currency), true);
|
||||
Money alreadyPaidEmi = rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount(), mc)
|
||||
.minus(rp.getFutureUnrecognizedInterest(), mc);
|
||||
Money newEmi = MathUtil.max(rp.getCalculatedDueInterest(), MathUtil.max(minimumNewEmi, alreadyPaidEmi, true), true);
|
||||
reallocationAmount.set(reallocationAmount.get().minus(rp.getEmi().minus(newEmi)));
|
||||
rp.setEmi(newEmi);
|
||||
rp.getInterestPeriods().forEach(InterestPeriod::updateOutstandingLoanBalance);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void calculateEMIOnActualModel(List<RepaymentPeriod> repaymentPeriods, ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
|
||||
+20
-5
@@ -33,7 +33,7 @@ public class EmiChangeOperation {
|
||||
DISBURSEMENT, //
|
||||
INTEREST_RATE_CHANGE, //
|
||||
CAPITALIZED_INCOME, //
|
||||
;
|
||||
ADD_REPAYMENT_PERIODS, //
|
||||
}
|
||||
|
||||
private final Action action;
|
||||
@@ -42,22 +42,37 @@ public class EmiChangeOperation {
|
||||
private final Money amount;
|
||||
private final BigDecimal interestRate;
|
||||
|
||||
private final int periodsToAdd;
|
||||
|
||||
public static EmiChangeOperation disburse(final LocalDate disbursementDueDate, final Money disbursedAmount) {
|
||||
return new EmiChangeOperation(EmiChangeOperation.Action.DISBURSEMENT, disbursementDueDate, disbursedAmount, null);
|
||||
return new EmiChangeOperation(EmiChangeOperation.Action.DISBURSEMENT, disbursementDueDate, disbursedAmount, null, 0);
|
||||
}
|
||||
|
||||
public static EmiChangeOperation changeInterestRate(final LocalDate newInterestSubmittedOnDate, final BigDecimal newInterestRate) {
|
||||
return new EmiChangeOperation(EmiChangeOperation.Action.INTEREST_RATE_CHANGE, newInterestSubmittedOnDate, null, newInterestRate);
|
||||
return new EmiChangeOperation(EmiChangeOperation.Action.INTEREST_RATE_CHANGE, newInterestSubmittedOnDate, null, newInterestRate, 0);
|
||||
}
|
||||
|
||||
public static EmiChangeOperation capitalizedIncome(final LocalDate transactionDueDate, final Money transactionAmount) {
|
||||
return new EmiChangeOperation(Action.CAPITALIZED_INCOME, transactionDueDate, transactionAmount, null);
|
||||
return new EmiChangeOperation(Action.CAPITALIZED_INCOME, transactionDueDate, transactionAmount, null, 0);
|
||||
}
|
||||
|
||||
public static EmiChangeOperation addRepaymentPeriods(final LocalDate transactionDueDate, final Money transactionAmount,
|
||||
final int numPeriods) {
|
||||
return new EmiChangeOperation(Action.ADD_REPAYMENT_PERIODS, transactionDueDate, transactionAmount, null, numPeriods);
|
||||
}
|
||||
|
||||
public EmiChangeOperation withZeroAmount() {
|
||||
if (action == Action.DISBURSEMENT || action == Action.CAPITALIZED_INCOME) {
|
||||
return new EmiChangeOperation(action, submittedOnDate, amount.zero(), null);
|
||||
return new EmiChangeOperation(action, submittedOnDate, amount.zero(), null, 0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isAddRepaymentPeriods() {
|
||||
return action == Action.ADD_REPAYMENT_PERIODS;
|
||||
}
|
||||
|
||||
public boolean isInterestRateChange() {
|
||||
return action == Action.INTEREST_RATE_CHANGE;
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -38,6 +38,7 @@ import java.util.TreeSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@@ -430,4 +431,13 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
public boolean isCopy() {
|
||||
return this.modifiers.get(COPY);
|
||||
}
|
||||
|
||||
public Function<Long, LocalDate> resolveRepaymentPEriodLengthGeneratorFunction(LocalDate instance) {
|
||||
return switch (loanProductRelatedDetail.getRepaymentPeriodFrequencyType()) {
|
||||
case MONTHS -> instance::plusMonths;
|
||||
case WEEKS -> instance::plusWeeks;
|
||||
case DAYS -> instance::plusDays;
|
||||
default -> throw new UnsupportedOperationException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+219
@@ -3953,6 +3953,225 @@ class ProgressiveEMICalculatorTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
public class RescheduleInstallmentExtendRepaymentPeriod {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.DECLINING_BALANCE);
|
||||
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod()).thenReturn(InterestCalculationPeriodMethod.DAILY);
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(BigDecimal.valueOf(12.0));
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraTermsDecliningBalance() {
|
||||
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(1000));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
|
||||
checkPeriod(interestSchedule, 1, 340.02, 6.70, 333.32, 336.66, false); //
|
||||
checkPeriod(interestSchedule, 2, 340.03, 3.37, 336.66, 0.00, false); //
|
||||
|
||||
// add extra repayment period 1
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 7, 2), 1);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
|
||||
checkPeriod(interestSchedule, 1, 227.81, 6.70, 221.11, 448.87, false); //
|
||||
checkPeriod(interestSchedule, 2, 227.81, 4.49, 223.32, 225.55, false); //
|
||||
checkPeriod(interestSchedule, 3, 227.81, 2.26, 225.55, 0.00, false); //
|
||||
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 8, 2), 2);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
|
||||
checkPeriod(interestSchedule, 1, 227.81, 6.70, 221.11, 448.87, false); //
|
||||
checkPeriod(interestSchedule, 2, 115.04, 4.49, 110.55, 338.32, false); //
|
||||
checkPeriod(interestSchedule, 3, 115.04, 3.38, 111.66, 226.66, false); //
|
||||
checkPeriod(interestSchedule, 4, 115.04, 2.27, 112.77, 113.89, false); //
|
||||
checkPeriod(interestSchedule, 5, 115.03, 1.14, 113.89, 0.00, false); //
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraTermsFlat() {
|
||||
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.FLAT);
|
||||
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod())
|
||||
.thenReturn(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(1000));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 666.67, false); //
|
||||
checkPeriod(interestSchedule, 1, 343.33, 10.00, 333.33, 333.34, false); //
|
||||
checkPeriod(interestSchedule, 2, 343.34, 10.00, 333.34, 0.00, false); //
|
||||
|
||||
// add extra repayment period 1
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 7, 2), 1);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 666.67, false); //
|
||||
checkPeriod(interestSchedule, 1, 232.22, 10.00, 222.22, 444.45, false); //
|
||||
checkPeriod(interestSchedule, 2, 232.22, 10.00, 222.22, 222.23, false); //
|
||||
checkPeriod(interestSchedule, 3, 232.23, 10.00, 222.23, 0.00, false); //
|
||||
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 8, 2), 2);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 666.67, false); //
|
||||
checkPeriod(interestSchedule, 1, 232.22, 10.00, 222.22, 444.45, false); //
|
||||
checkPeriod(interestSchedule, 2, 121.11, 10.00, 111.11, 333.34, false); //
|
||||
checkPeriod(interestSchedule, 3, 121.11, 10.00, 111.11, 222.23, false); //
|
||||
checkPeriod(interestSchedule, 4, 121.11, 10.00, 111.11, 111.12, false); //
|
||||
checkPeriod(interestSchedule, 5, 121.12, 10.00, 111.12, 0.00, false); //
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraTermsDecliningBalanceWithFullyRepaidPeriods() {
|
||||
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(1000));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 256.28, 10.00, 246.28, 753.72, false); //
|
||||
checkPeriod(interestSchedule, 1, 256.28, 7.54, 248.74, 504.98, false); //
|
||||
checkPeriod(interestSchedule, 2, 256.28, 5.05, 251.23, 253.75, false); //
|
||||
checkPeriod(interestSchedule, 3, 256.29, 2.54, 253.75, 0.00, false); //
|
||||
|
||||
// simulate merchant issued refund allocated on last installment ( fully repaid ) on first date
|
||||
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 10, 1), LocalDate.of(2024, 6, 1),
|
||||
Money.of(currency, BigDecimal.valueOf(256.29D)));
|
||||
|
||||
// simulate early repayment on day 0
|
||||
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 6, 1),
|
||||
Money.of(currency, BigDecimal.valueOf(256.28D)));
|
||||
|
||||
// verify 1st and 4th installments are fully repaid
|
||||
checkPeriod(interestSchedule, 0, 256.28, 0.00, 256.28, 487.43, true); //
|
||||
checkPeriod(interestSchedule, 1, 256.28, 9.74, 246.54, 240.89, false); //
|
||||
checkPeriod(interestSchedule, 2, 243.30, 2.41, 240.89, 0.00, false); //
|
||||
checkPeriod(interestSchedule, 3, 256.29, 0.00, 256.29, 0.00, true); //
|
||||
|
||||
// add extra repayment period 1
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 6, 15), 1);
|
||||
|
||||
// verify 1st and 4th installments are not modified, additional principal paid are substract from last
|
||||
// period
|
||||
checkPeriod(interestSchedule, 0, 256.28, 0.00, 256.28, 487.43, true); //
|
||||
checkPeriod(interestSchedule, 1, 206.04, 9.74, 196.30, 291.13, false); //
|
||||
checkPeriod(interestSchedule, 2, 206.04, 2.91, 203.13, 88.00, false); //
|
||||
checkPeriod(interestSchedule, 3, 256.29, 0.00, 256.29, 88.00, true); //
|
||||
checkPeriod(interestSchedule, 4, 89.76, 1.76, 88.00, 0.00, false); //
|
||||
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 6, 15), 2);
|
||||
|
||||
// verify 1st and 4th installments are not modified, additional principal paid are substract from last
|
||||
// periods
|
||||
checkPeriod(interestSchedule, 0, 256.28, 0.00, 256.28, 487.43, true); //
|
||||
checkPeriod(interestSchedule, 1, 126.91, 9.74, 117.17, 370.26, false); //
|
||||
checkPeriod(interestSchedule, 2, 126.91, 3.70, 123.21, 247.05, false); //
|
||||
checkPeriod(interestSchedule, 3, 256.29, 0.00, 256.29, 247.05, true); //
|
||||
checkPeriod(interestSchedule, 4, 126.91, 4.94, 121.97, 125.08, false); //
|
||||
checkPeriod(interestSchedule, 5, 126.33, 1.25, 125.08, 0.00, false); //
|
||||
checkPeriod(interestSchedule, 6, 0.00, 0.00, 0.00, 0.00, true); //
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraTermsFlatWithFullyRepaidPeriods() {
|
||||
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4);
|
||||
Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.FLAT);
|
||||
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod())
|
||||
.thenReturn(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(1000));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 750.00, false); //
|
||||
checkPeriod(interestSchedule, 1, 260.00, 10.00, 250.00, 500.00, false); //
|
||||
checkPeriod(interestSchedule, 2, 260.00, 10.00, 250.00, 250.00, false); //
|
||||
checkPeriod(interestSchedule, 3, 260.00, 10.00, 250.00, 0.00, false); //
|
||||
|
||||
// simulate merchant issued refund allocated on last installment ( fully repaid ) on first date
|
||||
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 10, 1), LocalDate.of(2024, 6, 1),
|
||||
Money.of(currency, BigDecimal.valueOf(250.0D)));
|
||||
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 10, 1), LocalDate.of(2024, 6, 1),
|
||||
Money.of(currency, BigDecimal.valueOf(10.0D)));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 500.00, false); //
|
||||
checkPeriod(interestSchedule, 1, 260.00, 10.00, 250.00, 250.00, false); //
|
||||
checkPeriod(interestSchedule, 2, 260.00, 10.00, 250.00, 0.00, false); //
|
||||
checkPeriod(interestSchedule, 3, 260.00, 10.00, 250.00, 0.00, true); //
|
||||
|
||||
// simulate early repayment on day 0
|
||||
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 6, 1),
|
||||
Money.of(currency, BigDecimal.valueOf(250.0D)));
|
||||
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 6, 1),
|
||||
Money.of(currency, BigDecimal.valueOf(10.0D)));
|
||||
|
||||
// verify 1st and 4th installments are fully repaid
|
||||
checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 500.00, true); //
|
||||
checkPeriod(interestSchedule, 1, 260.00, 10.00, 250.00, 250.00, false); //
|
||||
checkPeriod(interestSchedule, 2, 260.00, 10.00, 250.00, 0.00, false); //
|
||||
checkPeriod(interestSchedule, 3, 260.00, 10.00, 250.00, 0.00, true); //
|
||||
|
||||
// add extra repayment period 1
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 6, 15), 1);
|
||||
|
||||
// verify 1st and 4th installments are not modified, additional principal paid are substract from last
|
||||
// period
|
||||
checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 500.00, true); //
|
||||
checkPeriod(interestSchedule, 1, 210.00, 10.00, 200.00, 300.00, false); //
|
||||
checkPeriod(interestSchedule, 2, 210.00, 10.00, 200.00, 100.00, false); //
|
||||
checkPeriod(interestSchedule, 3, 260.00, 10.00, 250.00, 100.00, true); //
|
||||
checkPeriod(interestSchedule, 4, 110.00, 10.00, 100.00, 0.00, false); //
|
||||
|
||||
// add extra repayment period 2
|
||||
emiCalculator.addRepaymentPeriods(interestSchedule, LocalDate.of(2024, 6, 15), 2);
|
||||
|
||||
// verify 1st and 4th installments are not modified, additional principal paid are substract from last
|
||||
// periods
|
||||
checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 500.00, true); //
|
||||
checkPeriod(interestSchedule, 1, 152.86, 10.00, 142.86, 357.14, false); //
|
||||
checkPeriod(interestSchedule, 2, 152.86, 10.00, 142.86, 214.28, false); //
|
||||
checkPeriod(interestSchedule, 3, 260.00, 10.00, 250.00, 214.28, true); //
|
||||
checkPeriod(interestSchedule, 4, 152.86, 10.00, 142.86, 71.42, false); //
|
||||
checkPeriod(interestSchedule, 5, 81.42, 10.00, 71.42, 0.00, false); //
|
||||
checkPeriod(interestSchedule, 6, 10.00, 10.00, 0.00, 0.00, false); //
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// utilities
|
||||
private List<LoanScheduleModelRepaymentPeriod> generateExpectedRepaymentPeriods(LocalDate disbursementDate) {
|
||||
return switch (loanProductRelatedDetail.getRepaymentPeriodFrequencyType()) {
|
||||
|
||||
+1
-1
@@ -395,7 +395,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
.getLoanRescheduleRequestToTermVariationMappings()) {
|
||||
mapping.getLoanTermVariations().updateIsActive(true);
|
||||
LoanTermVariationType termType = mapping.getLoanTermVariations().getTermType();
|
||||
if (termType.isInterestRateVariation() || termType.isInterestRateFromInstallment()) {
|
||||
if (termType.isInterestRateVariation() || termType.isInterestRateFromInstallment() || termType.isExtendRepaymentPeriod()) {
|
||||
hasInterestRateChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user