FINERACT-1981: Fix interest calculation when applies multi disbursement

This commit is contained in:
Janos Meszaros
2025-01-20 13:39:09 +01:00
committed by Adam Saghy
parent 6e6ac8ffd6
commit 8ab9c9ea9a
6 changed files with 249 additions and 107 deletions
@@ -4082,10 +4082,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 750.54 | 249.46 | 5.68 | 0.0 | 0.0 | 255.14 | 255.14 | 255.14 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 22 January 2024 | 495.4 | 255.14 | 0.0 | 0.0 | 0.0 | 255.14 | 255.14 | 255.14 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 22 January 2024 | 240.26 | 255.14 | 0.0 | 0.0 | 0.0 | 255.14 | 255.14 | 255.14 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 22 January 2024 | 0.0 | 240.26 | 0.0 | 0.0 | 0.0 | 240.26 | 240.26 | 240.26 | 0.0 | 0.0 |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 750.53 | 249.47 | 5.68 | 0.0 | 0.0 | 255.15 | 255.15 | 255.15 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 22 January 2024 | 495.38 | 255.15 | 0.0 | 0.0 | 0.0 | 255.15 | 255.15 | 255.15 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 22 January 2024 | 240.23 | 255.15 | 0.0 | 0.0 | 0.0 | 255.15 | 255.15 | 255.15 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 22 January 2024 | 0.0 | 240.23 | 0.0 | 0.0 | 0.0 | 240.23 | 240.23 | 240.23 | 0.0 | 0.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 | 5.68 | 0.0 | 0.0 | 1005.68 | 1005.68 | 1005.68 | 0.0 | 0.0 |
@@ -4354,10 +4354,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 01 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | | 753.25 | 246.75 | 8.39 | 0.0 | 0.0 | 255.14 | 0.0 | 0.0 | 0.0 | 255.14 |
| 2 | 29 | 01 March 2024 | | 504.02 | 249.23 | 5.91 | 0.0 | 0.0 | 255.14 | 0.0 | 0.0 | 0.0 | 255.14 |
| 3 | 31 | 01 April 2024 | | 253.11 | 250.91 | 4.23 | 0.0 | 0.0 | 255.14 | 0.0 | 0.0 | 0.0 | 255.14 |
| 4 | 30 | 01 May 2024 | | 0.0 | 253.11 | 2.05 | 0.0 | 0.0 | 255.16 | 0.0 | 0.0 | 0.0 | 255.16 |
| 1 | 31 | 01 February 2024 | | 753.24 | 246.76 | 8.39 | 0.0 | 0.0 | 255.15 | 0.0 | 0.0 | 0.0 | 255.15 |
| 2 | 29 | 01 March 2024 | | 504.0 | 249.24 | 5.91 | 0.0 | 0.0 | 255.15 | 0.0 | 0.0 | 0.0 | 255.15 |
| 3 | 31 | 01 April 2024 | | 253.08 | 250.92 | 4.23 | 0.0 | 0.0 | 255.15 | 0.0 | 0.0 | 0.0 | 255.15 |
| 4 | 30 | 01 May 2024 | | 0.0 | 253.08 | 2.05 | 0.0 | 0.0 | 255.13 | 0.0 | 0.0 | 0.0 | 255.13 |
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.58 | 0.0 | 0.0 | 1020.58 | 0.0 | 0.0 | 0.0 | 1020.58 |
@@ -4371,10 +4371,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 01 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 750.54 | 249.46 | 5.68 | 0.0 | 0.0 | 255.14 | 255.14 | 255.14 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | | 500.7 | 249.84 | 5.3 | 0.0 | 0.0 | 255.14 | 247.7 | 247.7 | 0.0 | 7.44 |
| 3 | 31 | 01 April 2024 | | 249.76 | 250.94 | 4.2 | 0.0 | 0.0 | 255.14 | 0.0 | 0.0 | 0.0 | 255.14 |
| 4 | 30 | 01 May 2024 | | 0.0 | 249.76 | 2.03 | 0.0 | 0.0 | 251.79 | 0.0 | 0.0 | 0.0 | 251.79 |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 750.53 | 249.47 | 5.68 | 0.0 | 0.0 | 255.15 | 255.15 | 255.15 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | | 500.68 | 249.85 | 5.3 | 0.0 | 0.0 | 255.15 | 247.69 | 247.69 | 0.0 | 7.46 |
| 3 | 31 | 01 April 2024 | | 249.73 | 250.95 | 4.2 | 0.0 | 0.0 | 255.15 | 0.0 | 0.0 | 0.0 | 255.15 |
| 4 | 30 | 01 May 2024 | | 0.0 | 249.73 | 2.03 | 0.0 | 0.0 | 251.76 | 0.0 | 0.0 | 0.0 | 251.76 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 17.21 | 0.0 | 0.0 | 1017.21 | 502.84 | 502.84 | 0.0 | 514.37 |
@@ -4385,19 +4385,19 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| 22 January 2024 | Payout Refund | 500.0 | 494.32 | 5.68 | 0.0 | 0.0 | 505.68 | false | false |
| 22 January 2024 | Interest Refund | 2.84 | 2.84 | 0.0 | 0.0 | 0.0 | 502.84 | false | false |
When Admin sets the business date to "01 March 2024"
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 7.44 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 7.46 EUR transaction amount
When Admin sets the business date to "01 April 2024"
And Customer makes "AUTOPAY" repayment on "01 April 2024" with 255.14 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 April 2024" with 255.15 EUR transaction amount
When Admin sets the business date to "01 May 2024"
And Customer makes "AUTOPAY" repayment on "01 May 2024" with 251.79 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 May 2024" with 251.76 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 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 01 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 750.54 | 249.46 | 5.68 | 0.0 | 0.0 | 255.14 | 255.14 | 255.14 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 500.7 | 249.84 | 5.3 | 0.0 | 0.0 | 255.14 | 255.14 | 247.7 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 249.76 | 250.94 | 4.2 | 0.0 | 0.0 | 255.14 | 255.14 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 249.76 | 2.03 | 0.0 | 0.0 | 251.79 | 251.79 | 0.0 | 0.0 | 0.0 |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 750.53 | 249.47 | 5.68 | 0.0 | 0.0 | 255.15 | 255.15 | 255.15 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 500.68 | 249.85 | 5.3 | 0.0 | 0.0 | 255.15 | 255.15 | 247.69 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 249.73 | 250.95 | 4.2 | 0.0 | 0.0 | 255.15 | 255.15 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 249.73 | 2.03 | 0.0 | 0.0 | 251.76 | 251.76 | 0.0 | 0.0 | 0.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 | 17.21 | 0.0 | 0.0 | 1017.21 | 1017.21 | 502.84 | 0.0 | 0.0 |
@@ -4407,9 +4407,9 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| 01 January 2024 | Disbursement | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
| 22 January 2024 | Payout Refund | 500.0 | 494.32 | 5.68 | 0.0 | 0.0 | 505.68 | false | false |
| 22 January 2024 | Interest Refund | 2.84 | 2.84 | 0.0 | 0.0 | 0.0 | 502.84 | false | false |
| 01 March 2024 | Repayment | 7.44 | 2.14 | 5.3 | 0.0 | 0.0 | 500.7 | false | false |
| 01 April 2024 | Repayment | 255.14 | 250.94 | 4.2 | 0.0 | 0.0 | 249.76 | false | false |
| 01 May 2024 | Repayment | 251.79 | 249.76 | 2.03 | 0.0 | 0.0 | 0.0 | false | false |
| 01 March 2024 | Repayment | 7.46 | 2.16 | 5.3 | 0.0 | 0.0 | 500.68 | false | false |
| 01 April 2024 | Repayment | 255.15 | 250.95 | 4.2 | 0.0 | 0.0 | 249.73 | false | false |
| 01 May 2024 | Repayment | 251.76 | 249.73 | 2.03 | 0.0 | 0.0 | 0.0 | false | false |
| 01 May 2024 | Accrual | 17.21 | 0.0 | 17.21 | 0.0 | 0.0 | 0.0 | false | false |
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
@@ -4430,10 +4430,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 07 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 749.64 | 250.36 | 4.47 | 0.0 | 0.0 | 254.83 | 254.83 | 254.83 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | | 500.11 | 249.53 | 5.3 | 0.0 | 0.0 | 254.83 | 247.61 | 247.61 | 0.0 | 7.22 |
| 3 | 31 | 01 April 2024 | | 249.47 | 250.64 | 4.19 | 0.0 | 0.0 | 254.83 | 0.0 | 0.0 | 0.0 | 254.83 |
| 4 | 30 | 01 May 2024 | | 0.0 | 249.47 | 2.02 | 0.0 | 0.0 | 251.49 | 0.0 | 0.0 | 0.0 | 251.49 |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 749.63 | 250.37 | 4.47 | 0.0 | 0.0 | 254.84 | 254.84 | 254.84 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | | 500.09 | 249.54 | 5.3 | 0.0 | 0.0 | 254.84 | 247.6 | 247.6 | 0.0 | 7.24 |
| 3 | 31 | 01 April 2024 | | 249.44 | 250.65 | 4.19 | 0.0 | 0.0 | 254.84 | 0.0 | 0.0 | 0.0 | 254.84 |
| 4 | 30 | 01 May 2024 | | 0.0 | 249.44 | 2.02 | 0.0 | 0.0 | 251.46 | 0.0 | 0.0 | 0.0 | 251.46 |
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.98 | 0.0 | 0.0 | 1015.98 | 502.44 | 502.44 | 0.0 | 513.54 |
@@ -4444,19 +4444,19 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| 22 January 2024 | Merchant Issued Refund | 500.0 | 495.53 | 4.47 | 0.0 | 0.0 | 504.47 | false | false |
| 22 January 2024 | Interest Refund | 2.44 | 2.44 | 0.0 | 0.0 | 0.0 | 502.03 | false | false |
When Admin sets the business date to "01 March 2024"
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 7.22 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 7.24 EUR transaction amount
When Admin sets the business date to "01 April 2024"
And Customer makes "AUTOPAY" repayment on "01 April 2024" with 254.83 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 April 2024" with 254.84 EUR transaction amount
When Admin sets the business date to "01 May 2024"
And Customer makes "AUTOPAY" repayment on "01 May 2024" with 251.49 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 May 2024" with 251.46 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 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 07 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 749.64 | 250.36 | 4.47 | 0.0 | 0.0 | 254.83 | 254.83 | 254.83 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 500.11 | 249.53 | 5.3 | 0.0 | 0.0 | 254.83 | 254.83 | 247.61 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 249.47 | 250.64 | 4.19 | 0.0 | 0.0 | 254.83 | 254.83 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 249.47 | 2.02 | 0.0 | 0.0 | 251.49 | 251.49 | 0.0 | 0.0 | 0.0 |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 749.63 | 250.37 | 4.47 | 0.0 | 0.0 | 254.84 | 254.84 | 254.84 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 500.09 | 249.54 | 5.3 | 0.0 | 0.0 | 254.84 | 254.84 | 247.6 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 249.44 | 250.65 | 4.19 | 0.0 | 0.0 | 254.84 | 254.84 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 249.44 | 2.02 | 0.0 | 0.0 | 251.46 | 251.46 | 0.0 | 0.0 | 0.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 | 15.98 | 0.0 | 0.0 | 1015.98 | 1015.98 | 502.44 | 0.0 | 0.0 |
@@ -4466,9 +4466,9 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| 07 January 2024 | Disbursement | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
| 22 January 2024 | Merchant Issued Refund | 500.0 | 495.53 | 4.47 | 0.0 | 0.0 | 504.47 | false | false |
| 22 January 2024 | Interest Refund | 2.44 | 2.44 | 0.0 | 0.0 | 0.0 | 502.03 | false | false |
| 01 March 2024 | Repayment | 7.22 | 1.92 | 5.3 | 0.0 | 0.0 | 500.11 | false | false |
| 01 April 2024 | Repayment | 254.83 | 250.64 | 4.19 | 0.0 | 0.0 | 249.47 | false | false |
| 01 May 2024 | Repayment | 251.49 | 249.47 | 2.02 | 0.0 | 0.0 | 0.0 | false | false |
| 01 March 2024 | Repayment | 7.24 | 1.94 | 5.3 | 0.0 | 0.0 | 500.09 | false | false |
| 01 April 2024 | Repayment | 254.84 | 250.65 | 4.19 | 0.0 | 0.0 | 249.44 | false | false |
| 01 May 2024 | Repayment | 251.46 | 249.44 | 2.02 | 0.0 | 0.0 | 0.0 | false | false |
| 01 May 2024 | Accrual | 15.98 | 0.0 | 15.98 | 0.0 | 0.0 | 0.0 | false | false |
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
@@ -4487,10 +4487,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 07 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | | 752.34 | 247.66 | 7.17 | 0.0 | 0.0 | 254.83 | 0.0 | 0.0 | 0.0 | 254.83 |
| 2 | 29 | 01 March 2024 | | 503.41 | 248.93 | 5.9 | 0.0 | 0.0 | 254.83 | 0.0 | 0.0 | 0.0 | 254.83 |
| 3 | 31 | 01 April 2024 | | 252.8 | 250.61 | 4.22 | 0.0 | 0.0 | 254.83 | 0.0 | 0.0 | 0.0 | 254.83 |
| 4 | 30 | 01 May 2024 | | 0.0 | 252.8 | 2.05 | 0.0 | 0.0 | 254.85 | 0.0 | 0.0 | 0.0 | 254.85 |
| 1 | 31 | 01 February 2024 | | 752.33 | 247.67 | 7.17 | 0.0 | 0.0 | 254.84 | 0.0 | 0.0 | 0.0 | 254.84 |
| 2 | 29 | 01 March 2024 | | 503.39 | 248.94 | 5.9 | 0.0 | 0.0 | 254.84 | 0.0 | 0.0 | 0.0 | 254.84 |
| 3 | 31 | 01 April 2024 | | 252.77 | 250.62 | 4.22 | 0.0 | 0.0 | 254.84 | 0.0 | 0.0 | 0.0 | 254.84 |
| 4 | 30 | 01 May 2024 | | 0.0 | 252.77 | 2.05 | 0.0 | 0.0 | 254.82 | 0.0 | 0.0 | 0.0 | 254.82 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 19.34 | 0.0 | 0.0 | 1019.34 | 0.0 | 0.0 | 0.0 | 1019.34 |
@@ -4499,23 +4499,23 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| 01 January 2024 | Disbursement | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
| 07 January 2024 | Disbursement | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
When Admin sets the business date to "01 February 2024"
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 254.83 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 254.84 EUR transaction amount
When Admin sets the business date to "01 March 2024"
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 254.83 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 March 2024" with 254.84 EUR transaction amount
When Admin sets the business date to "01 April 2024"
And Customer makes "AUTOPAY" repayment on "01 April 2024" with 254.83 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 April 2024" with 254.84 EUR transaction amount
When Admin sets the business date to "01 May 2024"
And Customer makes "AUTOPAY" repayment on "01 May 2024" with 254.85 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 May 2024" with 254.82 EUR transaction amount
When Admin sets the business date to "10 May 2024"
When Customer makes "PAYOUT_REFUND" transaction with "AUTOPAY" payment type on "10 May 2024" with 500 EUR transaction amount and system-generated Idempotency key
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 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 07 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 752.34 | 247.66 | 7.17 | 0.0 | 0.0 | 254.83 | 254.83 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 503.41 | 248.93 | 5.9 | 0.0 | 0.0 | 254.83 | 254.83 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 252.8 | 250.61 | 4.22 | 0.0 | 0.0 | 254.83 | 254.83 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 252.8 | 2.05 | 0.0 | 0.0 | 254.85 | 254.85 | 0.0 | 0.0 | 0.0 |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 752.33 | 247.67 | 7.17 | 0.0 | 0.0 | 254.84 | 254.84 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 503.39 | 248.94 | 5.9 | 0.0 | 0.0 | 254.84 | 254.84 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 252.77 | 250.62 | 4.22 | 0.0 | 0.0 | 254.84 | 254.84 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 252.77 | 2.05 | 0.0 | 0.0 | 254.82 | 254.82 | 0.0 | 0.0 | 0.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 | 19.34 | 0.0 | 0.0 | 1019.34 | 1019.34 | 0.0 | 0.0 | 0.0 |
@@ -4523,10 +4523,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 01 January 2024 | Disbursement | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
| 07 January 2024 | Disbursement | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
| 01 February 2024 | Repayment | 254.83 | 247.66 | 7.17 | 0.0 | 0.0 | 752.34 | false | false |
| 01 March 2024 | Repayment | 254.83 | 248.93 | 5.9 | 0.0 | 0.0 | 503.41 | false | false |
| 01 April 2024 | Repayment | 254.83 | 250.61 | 4.22 | 0.0 | 0.0 | 252.8 | false | false |
| 01 May 2024 | Repayment | 254.85 | 252.8 | 2.05 | 0.0 | 0.0 | 0.0 | false | false |
| 01 February 2024 | Repayment | 254.84 | 247.67 | 7.17 | 0.0 | 0.0 | 752.33 | false | false |
| 01 March 2024 | Repayment | 254.84 | 248.94 | 5.9 | 0.0 | 0.0 | 503.39 | false | false |
| 01 April 2024 | Repayment | 254.84 | 250.62 | 4.22 | 0.0 | 0.0 | 252.77 | false | false |
| 01 May 2024 | Repayment | 254.82 | 252.77 | 2.05 | 0.0 | 0.0 | 0.0 | false | false |
| 01 May 2024 | Accrual | 19.34 | 0.0 | 19.34 | 0.0 | 0.0 | 0.0 | false | false |
| 10 May 2024 | Payout Refund | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
| 10 May 2024 | Interest Refund | 14.01 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
@@ -4536,10 +4536,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 07 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 752.34 | 247.66 | 7.17 | 0.0 | 0.0 | 254.83 | 254.83 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 503.41 | 248.93 | 5.9 | 0.0 | 0.0 | 254.83 | 254.83 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 252.8 | 250.61 | 4.22 | 0.0 | 0.0 | 254.83 | 254.83 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 252.8 | 2.05 | 0.0 | 0.0 | 254.85 | 254.85 | 0.0 | 0.0 | 0.0 |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 752.33 | 247.67 | 7.17 | 0.0 | 0.0 | 254.84 | 254.84 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 March 2024 | 503.39 | 248.94 | 5.9 | 0.0 | 0.0 | 254.84 | 254.84 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 252.77 | 250.62 | 4.22 | 0.0 | 0.0 | 254.84 | 254.84 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 01 May 2024 | 0.0 | 252.77 | 2.05 | 0.0 | 0.0 | 254.82 | 254.82 | 0.0 | 0.0 | 0.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 | 19.34 | 0.0 | 0.0 | 1019.34 | 1019.34 | 0.0 | 0.0 | 0.0 |
@@ -4547,10 +4547,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 01 January 2024 | Disbursement | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 | 250.0 | false | false |
| 07 January 2024 | Disbursement | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
| 01 February 2024 | Repayment | 254.83 | 247.66 | 7.17 | 0.0 | 0.0 | 752.34 | false | false |
| 01 March 2024 | Repayment | 254.83 | 248.93 | 5.9 | 0.0 | 0.0 | 503.41 | false | false |
| 01 April 2024 | Repayment | 254.83 | 250.61 | 4.22 | 0.0 | 0.0 | 252.8 | false | false |
| 01 May 2024 | Repayment | 254.85 | 252.8 | 2.05 | 0.0 | 0.0 | 0.0 | false | false |
| 01 February 2024 | Repayment | 254.84 | 247.67 | 7.17 | 0.0 | 0.0 | 752.33 | false | false |
| 01 March 2024 | Repayment | 254.84 | 248.94 | 5.9 | 0.0 | 0.0 | 503.39 | false | false |
| 01 April 2024 | Repayment | 254.84 | 250.62 | 4.22 | 0.0 | 0.0 | 252.77 | false | false |
| 01 May 2024 | Repayment | 254.82 | 252.77 | 2.05 | 0.0 | 0.0 | 0.0 | false | false |
| 01 May 2024 | Accrual | 19.34 | 0.0 | 19.34 | 0.0 | 0.0 | 0.0 | false | false |
| 10 May 2024 | Payout Refund | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
| 10 May 2024 | Interest Refund | 14.01 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
@@ -26,9 +26,12 @@ import java.math.MathContext;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.TreeSet;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -62,7 +65,8 @@ public class ProgressiveLoanInterestScheduleModel {
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf,
final MathContext mc) {
this.mc = mc;
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods);
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods,
(previousPeriod, repaymentPeriod) -> new RepaymentPeriod(previousPeriod, repaymentPeriod, mc));
this.interestRates = new TreeSet<>(interestRates);
this.loanProductRelatedDetail = loanProductRelatedDetail;
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
@@ -74,11 +78,20 @@ public class ProgressiveLoanInterestScheduleModel {
installmentAmountInMultiplesOf, mc);
}
private List<RepaymentPeriod> copyRepaymentPeriods(final List<RepaymentPeriod> repaymentPeriods) {
public ProgressiveLoanInterestScheduleModel emptyCopy() {
final List<RepaymentPeriod> repaymentPeriodCopies = copyRepaymentPeriods(repaymentPeriods,
(previousPeriod, repaymentPeriod) -> new RepaymentPeriod(previousPeriod, repaymentPeriod.getFromDate(),
repaymentPeriod.getDueDate(), repaymentPeriod.getEmi().zero(), mc));
return new ProgressiveLoanInterestScheduleModel(repaymentPeriodCopies, interestRates, loanProductRelatedDetail,
installmentAmountInMultiplesOf, mc);
}
private List<RepaymentPeriod> copyRepaymentPeriods(final List<RepaymentPeriod> repaymentPeriods,
final BiFunction<RepaymentPeriod, RepaymentPeriod, RepaymentPeriod> repaymentCopyFunction) {
final List<RepaymentPeriod> repaymentCopies = new ArrayList<>(repaymentPeriods.size());
RepaymentPeriod previousPeriod = null;
for (RepaymentPeriod repaymentPeriod : repaymentPeriods) {
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentPeriod, mc);
RepaymentPeriod currentPeriod = repaymentCopyFunction.apply(previousPeriod, repaymentPeriod);
previousPeriod = currentPeriod;
repaymentCopies.add(currentPeriod);
}
@@ -229,4 +242,40 @@ public class ProgressiveLoanInterestScheduleModel {
.filter(period -> isInPeriod(transactionDate, period.getFromDate(), period.getDueDate(), period.isFirstRepaymentPeriod()))//
.findFirst();
}
public boolean isEmpty() {
return repaymentPeriods.stream() //
.filter(rp -> !rp.getEmi().isZero()) //
.findFirst() //
.isEmpty(); //
}
/**
* This method gives you repayment pairs to copy attributes.
*
* @param periodFromDueDate
* Copy from this due periods.
* @param copyFromPeriods
* Copy source
* @param copyConsumer
* Consumer to copy attributes. Params: (from, to)
*/
public void copyPeriodsFrom(final LocalDate periodFromDueDate, List<RepaymentPeriod> copyFromPeriods,
BiConsumer<RepaymentPeriod, RepaymentPeriod> copyConsumer) {
if (copyFromPeriods.isEmpty()) {
return;
}
final Iterator<RepaymentPeriod> actualIterator = repaymentPeriods.iterator();
final Iterator<RepaymentPeriod> copyFromIterator = copyFromPeriods.iterator();
while (actualIterator.hasNext()) {
final RepaymentPeriod copyFromPeriod = copyFromIterator.next();
RepaymentPeriod actualPeriod = actualIterator.next();
while (actualIterator.hasNext() && !copyFromPeriod.getDueDate().isEqual(actualPeriod.getDueDate())) {
actualPeriod = actualIterator.next();
}
if (!actualPeriod.getDueDate().isBefore(periodFromDueDate)) {
copyConsumer.accept(copyFromPeriod, actualPeriod);
}
}
}
}
@@ -0,0 +1,49 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanproduct.calc;
import java.math.BigDecimal;
import java.time.LocalDate;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.fineract.organisation.monetary.domain.Money;
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class EmiChangeOperation {
public enum Action {
DISBURSEMENT, INTEREST_RATE_CHANGE
}
private final Action action;
private final LocalDate submittedOnDate;
private final Money amount;
private final BigDecimal interestRate;
public static EmiChangeOperation disburse(final LocalDate disbursementDueDate, final Money disbursedAmount) {
return new EmiChangeOperation(EmiChangeOperation.Action.DISBURSEMENT, disbursementDueDate, disbursedAmount, null);
}
public static EmiChangeOperation changeInterestRate(final LocalDate newInterestSubmittedOnDate, final BigDecimal newInterestRate) {
return new EmiChangeOperation(EmiChangeOperation.Action.INTEREST_RATE_CHANGE, newInterestSubmittedOnDate, null, newInterestRate);
}
}
@@ -102,9 +102,16 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
public void addDisbursement(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate disbursementDueDate,
final Money disbursedAmount) {
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(disbursementDueDate, disbursedAmount, scheduleModel.zero())
addDisbursement(scheduleModel, EmiChangeOperation.disburse(disbursementDueDate, disbursedAmount));
}
private void addDisbursement(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
scheduleModel
.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), operation.getAmount(),
scheduleModel.zero())
.ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors(
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, disbursementDueDate), scheduleModel));
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, operation.getSubmittedOnDate()), scheduleModel,
operation));
}
private LocalDate getEffectiveRepaymentDueDate(final ProgressiveLoanInterestScheduleModel scheduleModel,
@@ -125,13 +132,18 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
public void changeInterestRate(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate newInterestSubmittedOnDate,
final BigDecimal newInterestRate) {
final LocalDate interestRateChangeEffectiveDate = newInterestSubmittedOnDate.minusDays(1);
scheduleModel.addInterestRate(interestRateChangeEffectiveDate, newInterestRate);
changeInterestRate(scheduleModel, EmiChangeOperation.changeInterestRate(newInterestSubmittedOnDate, newInterestRate));
}
private void changeInterestRate(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
final LocalDate interestRateChangeEffectiveDate = operation.getSubmittedOnDate().minusDays(1);
scheduleModel.addInterestRate(interestRateChangeEffectiveDate, operation.getInterestRate());
scheduleModel
.changeOutstandingBalanceAndUpdateInterestPeriods(interestRateChangeEffectiveDate, scheduleModel.zero(),
scheduleModel.zero())
.ifPresent(repaymentPeriod -> calculateEMIValueAndRateFactors(
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, interestRateChangeEffectiveDate), scheduleModel));
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, interestRateChangeEffectiveDate), scheduleModel,
operation));
}
@Override
@@ -283,15 +295,24 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate Equal Monthly Installment value and Rate Factor -1 values for calculate Interest
*/
void calculateEMIValueAndRateFactors(final LocalDate calculateFromRepaymentPeriodDueDate,
final ProgressiveLoanInterestScheduleModel scheduleModel) {
private void calculateEMIValueAndRateFactors(final LocalDate calculateFromRepaymentPeriodDueDate,
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;
calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
calculateOutstandingBalance(scheduleModel);
calculateEMIOnPeriods(relatedRepaymentPeriods, scheduleModel);
if (onlyOnActualModelShouldApply) {
calculateEMIOnActualModel(relatedRepaymentPeriods, scheduleModel);
} else {
calculateEMIOnNewEmptyModelAndMerge(relatedRepaymentPeriods, scheduleModel, operation);
}
calculateOutstandingBalance(scheduleModel);
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, relatedRepaymentPeriods);
if (onlyOnActualModelShouldApply) {
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, relatedRepaymentPeriods);
}
}
private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel) {
@@ -378,19 +399,19 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*
* @return Rate Interest Rate in fraction format
*/
BigDecimal calcNominalInterestRatePercentage(final BigDecimal interestRate, MathContext mc) {
private BigDecimal calcNominalInterestRatePercentage(final BigDecimal interestRate, MathContext mc) {
return MathUtil.nullToZero(interestRate).divide(DIVISOR_100, mc);
}
/**
* * Calculate rate factors from ONLY repayment periods
*/
void calculateRateFactorForPeriods(final List<RepaymentPeriod> repaymentPeriods,
private void calculateRateFactorForPeriods(final List<RepaymentPeriod> repaymentPeriods,
final ProgressiveLoanInterestScheduleModel scheduleModel) {
repaymentPeriods.forEach(repaymentPeriod -> calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel));
}
void calculateRateFactorForRepaymentPeriod(final RepaymentPeriod repaymentPeriod,
private void calculateRateFactorForRepaymentPeriod(final RepaymentPeriod repaymentPeriod,
final ProgressiveLoanInterestScheduleModel scheduleModel) {
repaymentPeriod.getInterestPeriods().forEach(interestPeriod -> {
interestPeriod.setRateFactor(calculateRateFactorPerPeriod(scheduleModel, repaymentPeriod, interestPeriod.getFromDate(),
@@ -403,8 +424,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate Rate Factor for an exact Period
*/
BigDecimal calculateRateFactorPerPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel, final RepaymentPeriod repaymentPeriod,
final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) {
private BigDecimal calculateRateFactorPerPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel,
final RepaymentPeriod repaymentPeriod, final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) {
final MathContext mc = scheduleModel.mc();
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriodFromDate),
@@ -443,7 +464,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* @param interestPeriodDueDate
* @return
*/
BigDecimal calculatePeriodFractions(final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate, MathContext mc) {
private BigDecimal calculatePeriodFractions(final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate,
MathContext mc) {
BigDecimal cumulatedRateFactor = BigDecimal.ZERO;
int actualYear = interestPeriodFromDate.getYear();
int endYear = interestPeriodDueDate.getYear();
@@ -473,7 +495,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* @param calculatedDaysInPeriod
* @return
*/
BigDecimal calculateRateFactorPerPeriodBasedOnRepaymentFrequency(final BigDecimal interestRate,
private BigDecimal calculateRateFactorPerPeriodBasedOnRepaymentFrequency(final BigDecimal interestRate,
final PeriodFrequencyType repaymentFrequency, final BigDecimal repaymentEvery, final BigDecimal daysInMonth,
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
final MathContext mc) {
@@ -488,7 +510,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
};
}
void calculateEMIOnPeriods(final List<RepaymentPeriod> repaymentPeriods, final ProgressiveLoanInterestScheduleModel scheduleModel) {
private void calculateEMIOnActualModel(List<RepaymentPeriod> repaymentPeriods, ProgressiveLoanInterestScheduleModel scheduleModel) {
if (repaymentPeriods.isEmpty()) {
return;
}
@@ -496,7 +518,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
final BigDecimal rateFactorN = MathUtil.stripTrailingZeros(calculateRateFactorPlus1N(repaymentPeriods, mc));
final BigDecimal fnResult = MathUtil.stripTrailingZeros(calculateFnResult(repaymentPeriods, mc));
final RepaymentPeriod startPeriod = repaymentPeriods.get(0);
// TODO: double check
final Money outstandingBalance = startPeriod.getInitialBalanceForEmiRecalculation();
final Money equalMonthlyInstallment = Money.of(outstandingBalance.getCurrencyData(),
@@ -511,7 +533,22 @@ public final class ProgressiveEMICalculator implements EMICalculator {
});
}
Money applyInstallmentAmountInMultiplesOf(final ProgressiveLoanInterestScheduleModel scheduleModel,
private void calculateEMIOnNewEmptyModelAndMerge(List<RepaymentPeriod> repaymentPeriods,
ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
if (repaymentPeriods.isEmpty()) {
return;
}
final ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.emptyCopy();
addDisbursement(scheduleModelCopy, operation);
final LocalDate firstDueDate = repaymentPeriods.get(0).getDueDate();
scheduleModel.copyPeriodsFrom(firstDueDate, scheduleModelCopy.repaymentPeriods(), (newRepaymentPeriod, actualRepaymentPeriod) -> {
actualRepaymentPeriod.setEmi(actualRepaymentPeriod.getEmi().plus(newRepaymentPeriod.getEmi()));
actualRepaymentPeriod.setOriginalEmi(actualRepaymentPeriod.getOriginalEmi().plus(newRepaymentPeriod.getOriginalEmi()));
});
}
private Money applyInstallmentAmountInMultiplesOf(final ProgressiveLoanInterestScheduleModel scheduleModel,
final Money equalMonthlyInstallment) {
return scheduleModel.installmentAmountInMultiplesOf() != null
? Money.roundToMultiplesOf(equalMonthlyInstallment, scheduleModel.installmentAmountInMultiplesOf())
@@ -534,7 +571,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate Rate Factor Product from rate factors
*/
BigDecimal calculateRateFactorPlus1N(final List<RepaymentPeriod> periods, MathContext mc) {
private BigDecimal calculateRateFactorPlus1N(final List<RepaymentPeriod> periods, MathContext mc) {
return periods.stream().map(RepaymentPeriod::getRateFactorPlus1).reduce(BigDecimal.ONE,
(BigDecimal acc, BigDecimal value) -> acc.multiply(value, mc));
}
@@ -542,7 +579,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Summarize Fn values
*/
BigDecimal calculateFnResult(final List<RepaymentPeriod> periods, final MathContext mc) {
private BigDecimal calculateFnResult(final List<RepaymentPeriod> periods, final MathContext mc) {
return periods.stream()//
.skip(1)//
.map(RepaymentPeriod::getRateFactorPlus1)//
@@ -552,8 +589,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate the EMI (Equal Monthly Installment) value
*/
BigDecimal calculateEMIValue(final BigDecimal rateFactorPlus1N, final BigDecimal outstandingBalanceForRest, final BigDecimal fnResult,
MathContext mc) {
private BigDecimal calculateEMIValue(final BigDecimal rateFactorPlus1N, final BigDecimal outstandingBalanceForRest,
final BigDecimal fnResult, MathContext mc) {
return rateFactorPlus1N.multiply(outstandingBalanceForRest, mc).divide(fnResult, mc);
}
@@ -581,8 +618,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*
* @return Rate Factor for period
*/
BigDecimal rateFactorByRepaymentEveryDay(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInYear,
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
private BigDecimal rateFactorByRepaymentEveryDay(final BigDecimal interestRate, final BigDecimal repaymentEvery,
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
return rateFactorByRepaymentPeriod(interestRate, BigDecimal.ONE, repaymentEvery, daysInYear, actualDaysInPeriod,
calculatedDaysInPeriod, mc);
}
@@ -611,8 +648,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*
* @return Rate Factor for period
*/
BigDecimal rateFactorByRepaymentEveryWeek(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInYear,
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
private BigDecimal rateFactorByRepaymentEveryWeek(final BigDecimal interestRate, final BigDecimal repaymentEvery,
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
return rateFactorByRepaymentPeriod(interestRate, ONE_WEEK_IN_DAYS, repaymentEvery, daysInYear, actualDaysInPeriod,
calculatedDaysInPeriod, mc);
}
@@ -678,7 +715,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*
* @return Rate Factor for period
*/
BigDecimal rateFactorByRepaymentPeriod(final BigDecimal interestRate, final BigDecimal repaymentPeriodMultiplierInDays,
private BigDecimal rateFactorByRepaymentPeriod(final BigDecimal interestRate, final BigDecimal repaymentPeriodMultiplierInDays,
final BigDecimal repaymentEvery, final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod,
final BigDecimal calculatedDaysInPeriod, final MathContext mc) {
if (MathUtil.isZero(calculatedDaysInPeriod)) {
@@ -697,7 +734,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* Calculate Rate Factor based on Partial Period
*
*/
BigDecimal rateFactorByRepaymentPartialPeriod(final BigDecimal interestRate, final BigDecimal repaymentEvery,
private BigDecimal rateFactorByRepaymentPartialPeriod(final BigDecimal interestRate, final BigDecimal repaymentEvery,
final BigDecimal cumulatedPeriodRatio, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
final MathContext mc) {
if (MathUtil.isZero(calculatedDaysInPeriod)) {
@@ -953,14 +953,16 @@ class ProgressiveEMICalculatorTest {
disbursedAmount = toMoney(25.0);
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 8), disbursedAmount);
checkPeriod(interestSchedule, 0, 0, 29.94, 0.001019591398, 0.00, 1.15, 28.79, 146.21);
checkPeriod(interestSchedule, 0, 1, 29.94, 0.000764693548, 0.08, 1.15, 28.79, 146.21);
checkPeriod(interestSchedule, 0, 2, 29.94, 0.006117548387, 1.07, 1.15, 28.79, 146.21);
checkPeriod(interestSchedule, 1, 0, 29.94, 0.007901833333, 1.16, 28.78, 117.43);
checkPeriod(interestSchedule, 2, 0, 29.94, 0.007901833333, 0.93, 29.01, 88.42);
checkPeriod(interestSchedule, 3, 0, 29.94, 0.007901833333, 0.70, 29.24, 59.18);
checkPeriod(interestSchedule, 4, 0, 29.94, 0.007901833333, 0.47, 29.47, 29.71);
checkPeriod(interestSchedule, 5, 0, 29.94, 0.007901833333, 0.23, 29.71, 0.0);
checkTotalInterestDue(interestSchedule, 4.65);
checkPeriod(interestSchedule, 0, 0, 29.93, 0.001019591398, 0.00, 1.15, 28.78, 146.22);
checkPeriod(interestSchedule, 0, 1, 29.93, 0.000764693548, 0.08, 1.15, 28.78, 146.22);
checkPeriod(interestSchedule, 0, 2, 29.93, 0.006117548387, 1.07, 1.15, 28.78, 146.22);
checkPeriod(interestSchedule, 1, 0, 29.93, 0.007901833333, 1.16, 28.77, 117.45);
checkPeriod(interestSchedule, 2, 0, 29.93, 0.007901833333, 0.93, 29.00, 88.45);
checkPeriod(interestSchedule, 3, 0, 29.93, 0.007901833333, 0.70, 29.23, 59.22);
checkPeriod(interestSchedule, 4, 0, 29.93, 0.007901833333, 0.47, 29.46, 29.76);
checkPeriod(interestSchedule, 5, 0, 30.0, 0.007901833333, 0.24, 29.76, 0.0);
}
@Test
@@ -1034,13 +1036,13 @@ class ProgressiveEMICalculatorTest {
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 1, 1), toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 1), toMoney(44.85));
Assertions.assertEquals(0.0, toDouble(interestSchedule.getTotalDueInterest()));
checkTotalInterestDue(interestSchedule, 0.0);
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), toMoney(200.0));
Assertions.assertEquals(4.20, toDouble(interestSchedule.getTotalDueInterest()));
checkTotalInterestDue(interestSchedule, 4.11);
checkEmi(interestSchedule, 4, 84.03);
checkEmi(interestSchedule, 5, 84.05);
checkEmi(interestSchedule, 4, 85.05);
checkEmi(interestSchedule, 5, 78.86);
}
@Test
@@ -1398,6 +1400,11 @@ class ProgressiveEMICalculatorTest {
Assertions.assertEquals(emiValue, toDouble(interestScheduleModel.repaymentPeriods().get(repaymentIdx).getEmi()));
}
private static void checkTotalInterestDue(final ProgressiveLoanInterestScheduleModel interestScheduleModel,
final double totalInterestDue) {
Assertions.assertEquals(totalInterestDue, toDouble(interestScheduleModel.getTotalDueInterest()));
}
private static void checkPeriod(final ProgressiveLoanInterestScheduleModel interestScheduleModel, final int repaymentIdx,
final int interestIdx, final double emiValue, final double rateFactor, final double interestDue, final double principalDue,
final double remaingBalance) {
@@ -889,7 +889,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
transaction(171.29, "Repayment", "01 May 2021"), //
transaction(171.29, "Repayment", "01 June 2021"), //
transaction(171.32, "Repayment", "01 July 2021"), //
transaction(27.77, "Accrual", "01 July 2021") //
transaction(27.76, "Accrual", "01 July 2021") //
); //
});
runAt("11 July 2021", () -> {
@@ -909,8 +909,8 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
transaction(171.29, "Repayment", "01 June 2021"), //
transaction(171.32, "Repayment", "01 July 2021"), //
transaction(500.0, "Payout Refund", "11 July 2021"), //
transaction(20.41, "Interest Refund", "11 July 2021"), //
transaction(27.77, "Accrual", "01 July 2021") //
transaction(20.40, "Interest Refund", "11 July 2021"), //
transaction(27.76, "Accrual", "01 July 2021") //
); //
});
}