FINERACT-2354: Re-age - Excess payment handling - Default Behavior, interestRecalculation = true

This commit is contained in:
mariiaKraievska
2025-12-01 13:15:42 +01:00
committed by Adam Saghy
parent 2bde43ea83
commit b18912cc9d
11 changed files with 1696 additions and 567 deletions
File diff suppressed because it is too large Load Diff
@@ -535,17 +535,18 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 |
Then Loan Repayment schedule preview has 8 periods, with the following data for periods:
Then Loan Repayment schedule preview has 9 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 4 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
| 3 | 14 | 15 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 17 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 9 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 0.0 | 0.0 | 102.8 | 17.01 | 0.0 | 0.0 | 85.79 |
@@ -604,17 +605,18 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 |
Then Loan Repayment schedule preview has 8 periods, with the following data for periods:
Then Loan Repayment schedule preview has 9 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | | 83.57 | 0.0 | 0.0 | 10.0 | 0.0 | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 |
| 3 | 31 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 4 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
| 3 | 14 | 15 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 17 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 9 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 10.0 | 0.0 | 112.8 | 17.01 | 0.0 | 0.0 | 95.79 |
@@ -671,17 +673,18 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 |
Then Loan Repayment schedule preview has 8 periods, with the following data for periods:
Then Loan Repayment schedule preview has 9 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 4 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 10.0 | 0.0 | 24.3 | 0.0 | 0.0 | 0.0 | 24.3 |
| 8 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
| 3 | 14 | 15 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 17 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 10.0 | 0.0 | 24.3 | 0.0 | 0.0 | 0.0 | 24.3 |
| 9 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 10.0 | 0.0 | 112.8 | 17.01 | 0.0 | 0.0 | 95.79 |
@@ -735,17 +738,18 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 |
Then Loan Repayment schedule preview has 8 periods, with the following data for periods:
Then Loan Repayment schedule preview has 9 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 15 March 2024 | 75.58 | 7.99 | 0.0 | 0.0 | 0.0 | 7.99 | 7.99 | 7.99 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | | 63.53 | 12.05 | 0.88 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 4 | 30 | 01 May 2024 | | 50.97 | 12.56 | 0.37 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 5 | 31 | 01 June 2024 | | 38.34 | 12.63 | 0.3 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 6 | 30 | 01 July 2024 | | 25.63 | 12.71 | 0.22 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 7 | 31 | 01 August 2024 | | 12.85 | 12.78 | 0.15 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 8 | 31 | 01 September 2024| | 0.0 | 12.85 | 0.07 | 0.0 | 0.0 | 12.92 | 0.0 | 0.0 | 0.0 | 12.92 |
| 3 | 14 | 15 March 2024 | 15 March 2024 | 75.58 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 17 | 01 April 2024 | | 63.53 | 12.05 | 0.88 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 5 | 30 | 01 May 2024 | | 50.97 | 12.56 | 0.37 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 6 | 31 | 01 June 2024 | | 38.34 | 12.63 | 0.3 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 7 | 30 | 01 July 2024 | | 25.63 | 12.71 | 0.22 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 8 | 31 | 01 August 2024 | | 12.85 | 12.78 | 0.15 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 |
| 9 | 31 | 01 September 2024| | 0.0 | 12.85 | 0.07 | 0.0 | 0.0 | 12.92 | 0.0 | 0.0 | 0.0 | 12.92 |
Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.57 | 0.0 | 0.0 | 102.57 | 25.0 | 7.99 | 0.0 | 77.57 |
@@ -804,18 +808,19 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 |
Then Loan Repayment schedule preview has 9 periods, with the following data for periods:
Then Loan Repayment schedule preview has 10 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 |
| 2 | 31 | 01 February 2024 | 01 February 2024 | 62.68 | 12.32 | 0.44 | 0.0 | 0.0 | 12.76 | 12.76 | 0.0 | 0.0 | 0.0 |
| 3 | 29 | 01 March 2024 | 15 March 2024 | 62.68 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 31 | 01 April 2024 | | 52.69 | 9.99 | 0.74 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 5 | 30 | 01 May 2024 | | 42.27 | 10.42 | 0.31 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 6 | 31 | 01 June 2024 | | 31.79 | 10.48 | 0.25 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 7 | 30 | 01 July 2024 | | 21.25 | 10.54 | 0.19 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 8 | 31 | 01 August 2024 | | 10.64 | 10.61 | 0.12 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 9 | 31 | 01 September 2024 | | 0.0 | 10.64 | 0.06 | 0.0 | 0.0 | 10.7 | 0.0 | 0.0 | 0.0 | 10.7 |
| 4 | 14 | 15 March 2024 | 15 March 2024 | 62.68 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 17 | 01 April 2024 | | 52.69 | 9.99 | 0.74 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 6 | 30 | 01 May 2024 | | 42.27 | 10.42 | 0.31 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 7 | 31 | 01 June 2024 | | 31.79 | 10.48 | 0.25 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 8 | 30 | 01 July 2024 | | 21.25 | 10.54 | 0.19 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 9 | 31 | 01 August 2024 | | 10.64 | 10.61 | 0.12 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 |
| 10 | 31 | 01 September 2024 | | 0.0 | 10.64 | 0.06 | 0.0 | 0.0 | 10.7 | 0.0 | 0.0 | 0.0 | 10.7 |
Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.11 | 0.0 | 0.0 | 102.11 | 37.76 | 0.0 | 0.0 | 64.35 |
@@ -870,17 +875,18 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging transaction with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 |
Then Loan Repayment schedule has 8 periods, with the following data for periods:
Then Loan Repayment schedule has 9 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 4 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
| 3 | 14 | 15 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 17 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 9 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 0.0 | 0.0 | 102.8 | 17.01 | 0.0 | 0.0 | 85.79 |
@@ -912,17 +918,18 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 |
Then Loan Repayment schedule preview has 8 periods, with the following data for periods:
Then Loan Repayment schedule preview has 9 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 01 April 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 4 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
| 3 | 31 | 01 April 2024 | 01 April 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 0 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 9 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 0.0 | 0.0 | 102.8 | 17.01 | 0.0 | 0.0 | 85.79 |
@@ -1133,17 +1140,18 @@ Feature: LoanReAgingPreview
When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 |
Then Loan Repayment schedule preview has 8 periods, with the following data for periods:
Then Loan Repayment schedule preview has 9 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 4 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 10.0 | 0.0 | 24.3 | 0.0 | 0.0 | 0.0 | 24.3 |
| 6 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 7 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
| 3 | 14 | 15 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 17 | 01 April 2024 | | 70.25 | 13.32 | 0.98 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 5 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 6 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 10.0 | 0.0 | 24.3 | 0.0 | 0.0 | 0.0 | 24.3 |
| 7 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 8 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 |
| 9 | 31 | 01 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 |
Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 10.0 | 0.0 | 112.8 | 17.01 | 0.0 | 0.0 | 95.79 |
@@ -278,6 +278,13 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
null, null, null, false, false, true);
}
public static LoanRepaymentScheduleInstallment newInstallmentWithMovedPaidAmountDuringReAging(final Loan loan,
final Integer installmentNumber, final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principal,
final BigDecimal interest) {
return new LoanRepaymentScheduleInstallment(loan, installmentNumber, fromDate, dueDate, principal, interest, null, null, null, null,
null, null, false, false, false);
}
public static LoanRepaymentScheduleInstallment getLastNonDownPaymentInstallment(List<LoanRepaymentScheduleInstallment> installments) {
return installments.stream().filter(i -> !i.isDownPayment()).reduce((first, second) -> second).orElseThrow();
}
@@ -62,7 +62,8 @@ public class ProgressivePossibleNextRepaymentCalculationServiceImpl extends Abst
ctx.setWrittenOff(loan.isClosedWrittenOff());
ctx.setContractTerminated(loan.isContractTermination());
advancedPaymentScheduleTransactionProcessor.recalculateInterestForDate(nextPaymentDueDate, ctx, false);
RepaymentPeriod repaymentPeriod = scheduleModel.findRepaymentPeriodByDueDate(nextPaymentDueDate)
RepaymentPeriod repaymentPeriod = scheduleModel
.findRepaymentPeriodByFromAndDueDate(nextInstallment.getFromDate(), nextInstallment.getDueDate())
.orElseGet(scheduleModel::getLastRepaymentPeriod);
return repaymentPeriod.getOutstandingPrincipal().add(repaymentPeriod.getOutstandingInterest())
@@ -45,10 +45,10 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -365,10 +365,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateInstallmentIfInterestPeriodPresent(final ProgressiveLoanInterestScheduleModel scheduleModel,
final LoanRepaymentScheduleInstallment installment) {
emiCalculator.findRepaymentPeriod(scheduleModel, installment.getDueDate()).ifPresent(interestRepaymentPeriod -> {
installment.updateInterestCharged(interestRepaymentPeriod.getDueInterest().getAmount());
installment.updatePrincipal(interestRepaymentPeriod.getDuePrincipal().getAmount());
});
emiCalculator.findRepaymentPeriod(scheduleModel, installment.getFromDate(), installment.getDueDate())
.ifPresent(interestRepaymentPeriod -> {
installment.updateInterestCharged(interestRepaymentPeriod.getDueInterest().getAmount());
installment.updatePrincipal(interestRepaymentPeriod.getDuePrincipal().getAmount());
});
}
@Override
@@ -592,7 +593,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
if (repaidAmount.isGreaterThanZero()) {
emiCalculator.payPrincipal(model, currentInstallment.getDueDate(), transactionDate, repaidAmount);
emiCalculator.payPrincipal(model, currentInstallment.getFromDate(), currentInstallment.getDueDate(),
transactionDate, repaidAmount);
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
currentInstallment.payPrincipalComponent(transactionDate, repaidAmount);
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
@@ -629,7 +631,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
if (repaidAmount.isGreaterThanZero()) {
emiCalculator.payPrincipal(model, currentInstallment.getDueDate(), transactionDate, repaidAmount);
emiCalculator.payPrincipal(model, currentInstallment.getFromDate(), currentInstallment.getDueDate(),
transactionDate, repaidAmount);
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
currentInstallment.payPrincipalComponent(transactionDate, repaidAmount);
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
@@ -1581,7 +1584,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateInstallmentsPrincipalAndInterestByModel(ProgressiveTransactionCtx ctx) {
ctx.getModel().repaymentPeriods().forEach(repayment -> {
LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
.filter(ri -> !ri.isDownPayment() && Objects.equals(ri.getFromDate(), repayment.getFromDate())) //
.filter(ri -> !ri.isDownPayment() && Objects.equals(ri.getFromDate(), repayment.getFromDate()) //
&& Objects.equals(ri.getDueDate(), repayment.getDueDate()))
.findFirst().orElse(null);
if (installment != null) {
installment.updatePrincipal(repayment.getDuePrincipal().getAmount());
@@ -1774,7 +1778,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
&& loan.isInterestBearingAndInterestRecalculationEnabled()) {
final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
currentInstallment.getDueDate(), transactionDate, true).getAmount();
currentInstallment.getFromDate(), currentInstallment.getDueDate(), transactionDate, true).getAmount();
if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) {
currentInstallment.updateInterestCharged(newInterest);
}
@@ -1887,7 +1891,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
&& installment.getDueDate().isAfter(transactionDate)).forEach(installment -> {
final BigDecimal interestOutstanding = installment.getInterestOutstanding(currency).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
installment.getDueDate(), transactionDate, true).getAmount();
installment.getFromDate(), installment.getDueDate(), transactionDate, true).getAmount();
if (MathUtil.isGreaterThanZero(interestOutstanding) || MathUtil.isGreaterThanZero(newInterest)) {
final BigDecimal interestRemoved = MathUtil.subtract(MathUtil.nullToZero(installment.getInterestCharged()),
newInterest);
@@ -2503,10 +2507,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
loanTransactionToRepaymentScheduleMapping, charges, balances, LoanRepaymentScheduleInstallment.PaymentAction.PAY);
if (PRINCIPAL.equals(paymentAllocationType.getAllocationType())) {
emiCalculator.payPrincipal(model, installment.getDueDate(), payDate, paidPortion);
emiCalculator.payPrincipal(model, installment.getFromDate(), installment.getDueDate(), payDate, paidPortion);
updateRepaymentPeriods(loanTransaction, ctx);
} else if (INTEREST.equals(paymentAllocationType.getAllocationType())) {
emiCalculator.payInterest(model, installment.getDueDate(), payDate, paidPortion);
emiCalculator.payInterest(model, installment.getFromDate(), installment.getDueDate(), payDate, paidPortion);
updateRepaymentPeriods(loanTransaction, ctx);
}
return paidPortion;
@@ -2514,8 +2518,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateRepaymentPeriods(LoanTransaction loanTransaction, ProgressiveTransactionCtx ctx) {
ctx.getModel().repaymentPeriods().forEach(rm -> {
LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
.filter(ri -> ri.getDueDate().equals(rm.getDueDate()) && !ri.isDownPayment()).findFirst().orElse(null);
LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream().filter(
ri -> ri.getFromDate().equals(rm.getFromDate()) && ri.getDueDate().equals(rm.getDueDate()) && !ri.isDownPayment())
.findFirst().orElse(null);
if (installment != null) {
installment.updatePrincipal(rm.getDuePrincipal().getAmount());
installment.updateInterestCharged(rm.getDueInterest().getAmount());
@@ -2528,7 +2533,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateRepaymentPeriodBalances(PaymentAllocationType paymentAllocationType,
LoanRepaymentScheduleInstallment inAdvanceInstallment, ProgressiveTransactionCtx ctx, LocalDate payDate) {
PeriodDueDetails payableDetails = emiCalculator.getDueAmounts(ctx.getModel(), inAdvanceInstallment.getDueDate(), payDate);
PeriodDueDetails payableDetails = emiCalculator.getDueAmounts(ctx.getModel(), inAdvanceInstallment.getFromDate(),
inAdvanceInstallment.getDueDate(), payDate);
if (payableDetails.getDueInterest().isZero() && payableDetails.getDuePrincipal().isZero()) {
ctx.getSkipRepaymentScheduleInstallments().add(inAdvanceInstallment);
@@ -2777,16 +2783,17 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private Money fee;
private Money penalty;
private Money paidInAdvance;
private BigDecimal paidLate;
private Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings;
private BalancesWithPaidInAdvance(MonetaryCurrency currency) {
this(Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
new LinkedHashSet<>());
BigDecimal.ZERO, new LinkedHashSet<>());
}
private BalancesWithPaidInAdvance(LoanRepaymentScheduleInstallment i, MonetaryCurrency currency) {
this(i.getPrincipalCompleted(currency), i.getInterestPaid(currency), i.getFeeChargesPaid(currency),
i.getPenaltyChargesPaid(currency), i.getTotalPaidInAdvance(currency),
i.getPenaltyChargesPaid(currency), i.getTotalPaidInAdvance(currency), i.getTotalPaidLate(),
new LinkedHashSet<>(i.getLoanTransactionToRepaymentScheduleMappings()));
}
@@ -2796,7 +2803,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
set.addAll(a.getLoanTransactionToRepaymentScheduleMappings());
set.addAll(b.getLoanTransactionToRepaymentScheduleMappings());
return new BalancesWithPaidInAdvance(a.getPrincipal().add(b.getPrincipal()), a.getInterest().add(b.getInterest()),
a.getFee().add(b.getFee()), a.getPenalty().add(b.getPenalty()), a.getPaidInAdvance().add(b.getPaidInAdvance()), set);
a.getFee().add(b.getFee()), a.getPenalty().add(b.getPenalty()), a.getPaidInAdvance().add(b.getPaidInAdvance()),
MathUtil.nullToZero(a.getPaidLate()).add(MathUtil.nullToZero(b.getPaidLate())), set);
}
}
@@ -2901,58 +2909,118 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
}
}
private void cleanupInstallmentsAfterReAging(final ProgressiveTransactionCtx ctx) {
private void updateInstallmentsByModelForReAging(final LoanTransaction loanTransaction, final ProgressiveTransactionCtx ctx,
final LocalDate reAgePeriodStartDate) {
final List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
final LocalDate transactionDate = loanTransaction.getTransactionDate();
// Find the last re-aged installment number
final OptionalInt lastReAgedInstallmentNumberOpt = installments.stream().filter(LoanRepaymentScheduleInstallment::isReAged)
.mapToInt(LoanRepaymentScheduleInstallment::getInstallmentNumber).max();
final Optional<RepaymentPeriod> reAgeSpecialRepaymentPeriodOpt = ctx.getModel().repaymentPeriods().stream()
.filter(RepaymentPeriod::isReAgedEarlyRepaymentHolder).findFirst();
final LoanRepaymentScheduleInstallment installmentWithMovedPaidAmounts = reAgeSpecialRepaymentPeriodOpt
.map(repaymentPeriod -> createInstallmentWithMovedPaidAmounts(ctx, loanTransaction, installments, repaymentPeriod))
.orElse(null);
if (lastReAgedInstallmentNumberOpt.isPresent()) {
final int lastReAgedInstallmentNumber = lastReAgedInstallmentNumberOpt.getAsInt();
// Remove installments with numbers greater than the last re-aged installment
final List<LoanRepaymentScheduleInstallment> installmentsToRemove = installments.stream().filter(i -> i != null
&& !i.isAdditional() && i.getInstallmentNumber() != null && i.getInstallmentNumber() > lastReAgedInstallmentNumber)
.toList();
installmentsToRemove.forEach(installments::remove);
}
}
final List<LoanCharge> liftedLoanCharges = new ArrayList<>();
installments.stream().filter(installment -> !installment.getDueDate().isBefore(transactionDate)).forEach(i -> {
liftedLoanCharges.addAll(getLoanChargesOfInstallment(ctx.getCharges(), i, 1));
});
installments.removeIf(i -> i.getInstallmentNumber() != null && !i.isDownPayment() && !i.getDueDate().isBefore(transactionDate));
final ListIterator<LoanRepaymentScheduleInstallment> iterator = installments.listIterator();
final AtomicInteger installmentCounter = new AtomicInteger();
private void updateInstallmentsByModelForReAging(final LoanTransaction loanTransaction, final ProgressiveTransactionCtx ctx) {
ctx.getModel().repaymentPeriods().forEach(rp -> {
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
.filter(ri -> ri.getFromDate().equals(rp.getFromDate()) && !ri.isDownPayment()).findFirst().orElse(null);
final LocalDate transactionDate = loanTransaction.getTransactionDate();
LoanRepaymentScheduleInstallment installment = null;
while (iterator.hasNext() && (installment == null || installment.isAdditional() || installment.isDownPayment())) {
installment = iterator.next();
installmentCounter.getAndIncrement();
}
if (installment != null) {
installment.setFromDate(rp.getFromDate());
installment.setDueDate(rp.getDueDate());
if (installment.isNotFullyPaidOff() || !installment.getFromDate().isBefore(reAgePeriodStartDate)) {
installment.setFromDate(rp.getFromDate());
installment.setDueDate(rp.getDueDate());
if (rp.getEmi().isZero()) {
installment.updatePrincipal(BigDecimal.ZERO);
installment.updateInterestCharged(BigDecimal.ZERO);
} else {
installment.updatePrincipal(rp.getDuePrincipal().getAmount());
installment.updateInterestCharged(rp.getDueInterest().getAmount());
if (rp.getEmi().isZero()) {
installment.updatePrincipal(ZERO);
installment.updateInterestCharged(ZERO);
} else {
installment.updatePrincipal(rp.getDuePrincipal().getAmount());
installment.updateInterestCharged(rp.getDueInterest().getAmount());
}
if (!installment.getFromDate().isBefore(reAgePeriodStartDate)) {
installment.setReAged(true);
}
installment.setAdditional(false);
installment.updateObligationsMet(ctx.getCurrency(), transactionDate);
}
installment.setReAged(true);
installment.setAdditional(false);
installment.updateObligationsMet(ctx.getCurrency(), transactionDate);
} else {
final LoanRepaymentScheduleInstallment lastInstallment = ctx.getInstallments().getLast();
final LoanRepaymentScheduleInstallment newInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(
loanTransaction.getLoan(), lastInstallment.getInstallmentNumber() + 1, rp.getFromDate(), rp.getDueDate(),
rp.getDuePrincipal().getAmount(), null, null, null);
if (rp.isReAgedEarlyRepaymentHolder()) {
iterator.add(installmentWithMovedPaidAmounts);
} else {
installmentCounter.getAndIncrement();
final LoanRepaymentScheduleInstallment newInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(
loanTransaction.getLoan(), installmentCounter.get(), rp.getFromDate(), rp.getDueDate(),
rp.getDuePrincipal().getAmount(), null, null, null);
if (rp.getDueInterest().isGreaterThanZero()) {
newInstallment.addToInterest(transactionDate, rp.getDueInterest());
if (rp.getDueInterest().isGreaterThanZero()) {
newInstallment.addToInterest(transactionDate, rp.getDueInterest());
}
newInstallment.updateObligationsMet(ctx.getCurrency(), transactionDate);
iterator.add(newInstallment);
}
newInstallment.updateObligationsMet(ctx.getCurrency(), transactionDate);
ctx.getInstallments().add(newInstallment);
}
});
cleanupInstallmentsAfterReAging(ctx);
for (LoanCharge loanCharge : liftedLoanCharges) {
loanChargeRepaymentScheduleProcessing.reprocess(ctx.getCurrency(), loanTransaction.getLoan().getDisbursementDate(),
installments, loanCharge);
}
}
/**
* Creates a special installment to preserve paid amounts during the re-aging process.
*
* @param loanTransaction
* the re-aging transaction
* @param installments
* list of existing loan repayment schedule installments
* @param rp
* repayment period with moved paid amounts
* @return new installment with moved paid amounts
*/
private LoanRepaymentScheduleInstallment createInstallmentWithMovedPaidAmounts(final ProgressiveTransactionCtx ctx,
final LoanTransaction loanTransaction, final List<LoanRepaymentScheduleInstallment> installments, final RepaymentPeriod rp) {
final MonetaryCurrency currency = loanTransaction.getLoan().loanCurrency();
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final Optional<LoanRepaymentScheduleInstallment> firstReAgedInstallment = installments.stream()
.filter(i -> (i.getInstallmentNumber() != null && !i.isDownPayment() && !i.getDueDate().isBefore(transactionDate)))
.findFirst();
int installmentNumberToInsertPaidInstallment = installments.getLast().getInstallmentNumber() + 1;
if (firstReAgedInstallment.isPresent()) {
installmentNumberToInsertPaidInstallment = firstReAgedInstallment.get().getInstallmentNumber();
}
final LoanRepaymentScheduleInstallment newInstallment = LoanRepaymentScheduleInstallment
.newInstallmentWithMovedPaidAmountDuringReAging(loanTransaction.getLoan(), installmentNumberToInsertPaidInstallment,
rp.getFromDate(), rp.getDueDate(), rp.getDuePrincipal().getAmount(), rp.getDueInterest().getAmount());
newInstallment.payPrincipalComponent(transactionDate, rp.getPaidPrincipal());
newInstallment.payInterestComponent(transactionDate, rp.getPaidInterest());
final BalancesWithPaidInAdvance paidInAdvanceBalances = liftEarlyRepaidBalances(installments, transactionDate, currency,
ctx.getAlreadyProcessedTransactions());
newInstallment.setTotalPaidInAdvance(paidInAdvanceBalances.getPaidInAdvance().getAmount());
newInstallment.setTotalPaidLate(paidInAdvanceBalances.getPaidLate());
paidInAdvanceBalances.loanTransactionToRepaymentScheduleMappings.forEach(m -> m.setInstallment(newInstallment));
newInstallment.updateObligationsMet(currency, transactionDate);
return newInstallment;
}
private void reprocessInstallments(final List<LoanRepaymentScheduleInstallment> installments) {
@@ -3030,8 +3098,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
final BigDecimal totalPrincipal = periodsToRemove.stream().map(rp -> rp.getDuePrincipal().getAmount()).reduce(BigDecimal.ZERO,
BigDecimal::add);
final BigDecimal newInterest = emiCalculator
.getPeriodInterestTillDate(transactionCtx.getModel(), lastPeriod.getDueDate(), transactionDate, false).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(transactionCtx.getModel(), lastPeriod.getFromDate(),
lastPeriod.getDueDate(), transactionDate, false).getAmount();
lastPeriod.setEmi(lastPeriod.getDuePrincipal().add(totalPrincipal).add(newInterest));
@@ -3074,9 +3142,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
} else if (isInPeriod) {
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
&& loan.isInterestBearingAndInterestRecalculationEnabled()) {
interest = emiCalculator
.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), installment.getDueDate(), chargeOffDate, true)
.getAmount();
interest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), installment.getFromDate(),
installment.getDueDate(), chargeOffDate, true).getAmount();
} else {
final BigDecimal totalInterest = installment.getInterestOutstanding(currency).getAmount();
if (LoanChargeOffBehaviour.ZERO_INTEREST.equals(loan.getLoanProductRelatedDetail().getChargeOffBehaviour())
@@ -3500,18 +3567,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
final List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
final LocalDate reAgingStartDate = loanTransaction.getLoanReAgeParameter().getStartDate();
final List<LoanRepaymentScheduleInstallment> installmentsBeforeReAging = installments.stream()
.filter(rp -> rp.getFromDate().isBefore(reAgingStartDate)).toList();
// Define the date, which must match reAgingStartDate
final LocalDate expectedReAgingDate = installmentsBeforeReAging.isEmpty() ? installments.getFirst().getFromDate()
: installmentsBeforeReAging.getLast().getDueDate();
if (!expectedReAgingDate.isEqual(reAgingStartDate)) {
// TODO: implement logic when re-aging changes the due dates
throw new NotImplementedException("Logic when re-aging changes the due dates not implemented");
}
final Money interestFromZeroedInstallments = emiCalculator.getOutstandingInterestTillDate(ctx.getModel(),
loanTransaction.getTransactionDate());
@@ -3537,13 +3592,13 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
loan.getLoanProduct().getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalculation())
.mc(mc).build();
LocalDate reAgePeriodStartDate = calculateFirstReAgedPeriodStartDate(loanTransaction);
LocalDate reageFirstDueDate = loanTransaction.getLoanReAgeParameter().getStartDate();
final LocalDate reAgePeriodStartDate = calculateFirstReAgedPeriodStartDate(loanTransaction);
final LocalDate reAgeFirstDueDate = loanTransaction.getLoanReAgeParameter().getStartDate();
// Update the existing model with re-aged periods
emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx.getModel(), reAgePeriodStartDate, reageFirstDueDate,
emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx.getModel(), reAgePeriodStartDate, reAgeFirstDueDate,
loanTransaction.getTransactionDate(), loanApplicationTerms, mc);
updateInstallmentsByModelForReAging(loanTransaction, ctx);
updateInstallmentsByModelForReAging(loanTransaction, ctx, reAgePeriodStartDate);
loanTransaction.updateComponentsAndTotal(totalOutstandingPrincipal, interestFromZeroedInstallments, Money.zero(currency),
Money.zero(currency));
@@ -126,19 +126,20 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
chargesDueAtTimeOfDisbursement, false, mc);
repaymentPeriod.setPeriodNumber(scheduleParams.getInstalmentNumber());
emiCalculator.findRepaymentPeriod(interestScheduleModel, repaymentPeriod.getDueDate()).ifPresent(interestRepaymentPeriod -> {
final Money principalDue = interestRepaymentPeriod.getDuePrincipal();
final Money interestDue = interestRepaymentPeriod.getDueInterest();
emiCalculator.findRepaymentPeriod(interestScheduleModel, repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate())
.ifPresent(interestRepaymentPeriod -> {
final Money principalDue = interestRepaymentPeriod.getDuePrincipal();
final Money interestDue = interestRepaymentPeriod.getDueInterest();
repaymentPeriod.addPrincipalAmount(principalDue);
repaymentPeriod.addInterestAmount(interestDue);
repaymentPeriod.setOutstandingLoanBalance(interestRepaymentPeriod.getOutstandingLoanBalance());
repaymentPeriod.addPrincipalAmount(principalDue);
repaymentPeriod.addInterestAmount(interestDue);
repaymentPeriod.setOutstandingLoanBalance(interestRepaymentPeriod.getOutstandingLoanBalance());
scheduleParams.addTotalCumulativePrincipal(principalDue);
scheduleParams.addTotalCumulativeInterest(interestDue);
// add everything
scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue, mc));
});
scheduleParams.addTotalCumulativePrincipal(principalDue);
scheduleParams.addTotalCumulativeInterest(interestDue);
// add everything
scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue, mc));
});
applyChargesForCurrentPeriod(repaymentPeriod, loanCharges, scheduleParams, currency, mc);
periods.add(repaymentPeriod);
@@ -254,7 +255,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
}
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate);
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
return emiCalculator.getPeriodInterestTillDate(model, installment.getDueDate(), targetDate, false);
return emiCalculator.getPeriodInterestTillDate(model, installment.getFromDate(), installment.getDueDate(), targetDate, false);
}
// Private, internal methods
@@ -61,7 +61,8 @@ public interface EMICalculator {
/**
* Find repayment period based on Due Date.
*/
Optional<RepaymentPeriod> findRepaymentPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate dueDate);
Optional<RepaymentPeriod> findRepaymentPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate fromDate,
LocalDate dueDate);
/**
* Applies the disbursement on the interest model. This method recalculates the EMI amounts from the action date.
@@ -95,14 +96,14 @@ public interface EMICalculator {
/**
* This method used for pay interest portion during the repayment transaction.
*/
void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate,
Money interestAmount);
void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money interestAmount);
/**
* This method used for pay principal portion during the repayment transaction.
*/
void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate,
Money principalAmount);
void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money principalAmount);
/**
* This method used for credit principal portion. This method increases the outstanding balance. This method creates
@@ -120,15 +121,15 @@ public interface EMICalculator {
* This method gives back the maximum of the due principal and maximum of the due interest for a requested day.
*/
@NotNull
PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
@NotNull LocalDate targetDate);
PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate);
/**
* Gives back the sum of the interest from the whole model on the given date.
*/
@NotNull
Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
@NotNull LocalDate targetDate, boolean includeChargebackInterest);
Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate, boolean includeChargebackInterest);
Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel interestScheduleModel, LocalDate targetDate);
@@ -112,11 +112,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
public Optional<RepaymentPeriod> findRepaymentPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel,
final LocalDate repaymentPeriodDueDate) {
final LocalDate repaymentPeriodFromDate, final LocalDate repaymentPeriodDueDate) {
if (scheduleModel == null) {
return Optional.empty();
}
return scheduleModel.findRepaymentPeriodByDueDate(repaymentPeriodDueDate);
return scheduleModel.findRepaymentPeriodByFromAndDueDate(repaymentPeriodFromDate, repaymentPeriodDueDate);
}
/**
@@ -263,9 +263,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}
@Override
public void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate,
Money interestAmount) {
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate);
public void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money interestAmount) {
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodFromDate, repaymentPeriodDueDate);
Optional<RepaymentPeriod> latestNotLastOpenRepaymentPeriodBeforeDate = getLatestNotLastOpenRepaymentPeriodBeforeDate(scheduleModel,
transactionDate);
@@ -280,12 +280,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}
@Override
public void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate,
LocalDate transactionDate, Money principalAmount) {
public void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money principalAmount) {
if (MathUtil.isEmpty(principalAmount)) {
return;
}
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate);
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodFromDate, repaymentPeriodDueDate);
repaymentPeriod.ifPresent(rp -> rp.addPaidPrincipalAmount(principalAmount));
// If it is paid late, we need to calculate with the period due date
LocalDate balanceCorrectionDate = DateUtils.isBefore(repaymentPeriodDueDate, transactionDate) ? repaymentPeriodDueDate
@@ -379,11 +379,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*/
@Override
@NotNull
public PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
@NotNull LocalDate targetDate) {
public PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate) {
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
targetDate);
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow();
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate
.findRepaymentPeriodByFromAndDueDate(periodFromDate, periodDueDate).orElseThrow();
long notFullyRepaidRepaymentPeriodCount = recalculatedScheduleModelTillDate.repaymentPeriods().stream()
.filter(rp -> !rp.isFullyPaid()).count();
boolean multiplePeriodIsUnpaid = notFullyRepaidRepaymentPeriodCount > 1L;
@@ -413,11 +414,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
@NotNull
public Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
@NotNull LocalDate targetDate, boolean includeCreditedInterest) {
public Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate, boolean includeCreditedInterest) {
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
targetDate);
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow();
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate
.findRepaymentPeriodByFromAndDueDate(periodFromDate, periodDueDate).orElseThrow();
return includeCreditedInterest ? repaymentPeriod.getCalculatedDueInterest()
: repaymentPeriod.getCalculatedDueInterest().minus(repaymentPeriod.getCreditedInterest(),
recalculatedScheduleModelTillDate.mc());
@@ -572,15 +574,20 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
public void updateModelRepaymentPeriodsDuringReAge(final ProgressiveLoanInterestScheduleModel scheduleModel,
final LocalDate reAgePeriodStartDate, LocalDate reageFirstDueDate, LocalDate targetDate,
final LocalDate reAgePeriodStartDate, final LocalDate reAgeFirstDueDate, final LocalDate targetDate,
final LoanApplicationTerms loanApplicationTerms, final MathContext mc) {
moveOutstandingAmountsFromPeriodsBeforeReAging(scheduleModel.repaymentPeriods(), targetDate);
moveOutstandingAmountsFromPeriodsBeforeReAging(scheduleModel.repaymentPeriods(), reageFirstDueDate);
// calculate already paid balances from transaction date
final OutstandingDetails paidBalancesFromTransactionDate = calculatePaidBalancesAfterDate(scheduleModel, targetDate);
accelerateMaturityDateTo(scheduleModel, targetDate);
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generateTemporaryReAgedScheduleModel(loanApplicationTerms,
mc, reAgePeriodStartDate);
mergeNewInterestScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, reageFirstDueDate, targetDate);
mergeNewInterestScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, reAgeFirstDueDate, targetDate,
paidBalancesFromTransactionDate);
}
@Override
@@ -634,7 +641,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
private List<RepaymentPeriod> findPossiblyOverdueRepaymentPeriods(final LocalDate targetDate,
final ProgressiveLoanInterestScheduleModel model) {
return model.repaymentPeriods().stream() //
.filter(repaymentPeriod -> DateUtils.isAfter(targetDate, repaymentPeriod.getDueDate())).toList();
.filter(repaymentPeriod -> !repaymentPeriod.isReAgedEarlyRepaymentHolder()
&& DateUtils.isAfter(targetDate, repaymentPeriod.getDueDate()))
.toList();
}
private boolean adjustOverduePrincipal(final LocalDate currentDate, final RepaymentPeriod currentInstallment,
@@ -694,8 +703,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* significantly from other periods.
*/
private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveLoanInterestScheduleModel scheduleModel,
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LocalDate reageFirstDueDate,
LocalDate targetDate) {
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LocalDate reAgeFirstDueDate,
final LocalDate targetDate, final OutstandingDetails paidBalancesFromTransactionDate) {
final List<RepaymentPeriod> newPeriods = temporaryReAgedScheduleModel.repaymentPeriods();
if (newPeriods.isEmpty()) {
@@ -706,46 +715,44 @@ public final class ProgressiveEMICalculator implements EMICalculator {
final RepaymentPeriod firstRepaymentPeriod = existingRepaymentPeriods.getFirst();
final Money disbursedAmount = firstRepaymentPeriod.getTotalDisbursedAmount();
final Optional<RepaymentPeriod> firstExistingReAgedRepaymentPeriodOpt = existingRepaymentPeriods.stream()
.filter(period -> period.getDueDate().equals(reageFirstDueDate)).findFirst();
if (!paidBalancesFromTransactionDate.getOutstandingInterest().isZero()
|| !paidBalancesFromTransactionDate.getOutstandingPrincipal().isZero()) {
createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(scheduleModel,
paidBalancesFromTransactionDate.getOutstandingPrincipal(), paidBalancesFromTransactionDate.getOutstandingInterest(),
false);
}
for (final RepaymentPeriod newPeriod : newPeriods) {
final Optional<RepaymentPeriod> existingRepaymentPeriodOpt = existingRepaymentPeriods.stream().filter(
period -> period.getFromDate().equals(newPeriod.getFromDate()) && period.getDueDate().equals(newPeriod.getDueDate()))
.findFirst();
Optional<RepaymentPeriod> previousExistingRepaymentPeriodOpt = Optional.empty();
if (existingRepaymentPeriodOpt.isPresent() && firstExistingReAgedRepaymentPeriodOpt.isPresent()
&& existingRepaymentPeriodOpt.get().equals(firstExistingReAgedRepaymentPeriodOpt.get())) {
previousExistingRepaymentPeriodOpt = existingRepaymentPeriodOpt.get().getPrevious();
}
final Money newPrincipal = newPeriod.getDuePrincipal();
final Money newInterest = newPeriod.getDueInterest();
final RepaymentPeriod rp = RepaymentPeriod.create(
previousExistingRepaymentPeriodOpt.orElseGet(existingRepaymentPeriods::getLast), newPeriod.getFromDate(),
newPeriod.getDueDate(), newPrincipal.add(newInterest), scheduleModel.mc(), scheduleModel.loanProductRelatedDetail());
rp.setTotalDisbursedAmount(disbursedAmount);
if (reAgeFirstDueDate.isEqual(scheduleModel.getStartDate()) && newPeriod.equals(newPeriods.getFirst())) {
final RepaymentPeriod firstPeriod = existingRepaymentPeriods.getFirst();
firstPeriod.setDueDate(newPeriod.getDueDate());
firstPeriod.getInterestPeriods().getLast().setDueDate(newPeriod.getDueDate());
firstPeriod.setEmi(newPrincipal.add(newInterest));
calculateRateFactorForRepaymentPeriod(firstRepaymentPeriod, scheduleModel);
} else {
final RepaymentPeriod rp = RepaymentPeriod.create(
!existingRepaymentPeriods.isEmpty() ? existingRepaymentPeriods.getLast() : null,
(newPeriod.equals(newPeriods.getFirst()) && !existingRepaymentPeriods.isEmpty())
? existingRepaymentPeriods.getLast().getDueDate()
: newPeriod.getFromDate(),
newPeriod.getDueDate(), newPrincipal.add(newInterest), scheduleModel.mc(),
scheduleModel.loanProductRelatedDetail());
rp.setTotalDisbursedAmount(disbursedAmount);
existingRepaymentPeriodOpt.ifPresent(existingRepaymentPeriods::remove);
existingRepaymentPeriods.add(rp);
calculateRateFactorForRepaymentPeriod(rp, scheduleModel);
existingRepaymentPeriods.add(rp);
calculateRateFactorForRepaymentPeriod(rp, scheduleModel);
}
}
final List<RepaymentPeriod> reAgedRepaymentPeriods = existingRepaymentPeriods.stream()
.filter(repaymentPeriod -> (!repaymentPeriod.getFromDate().isBefore(reageFirstDueDate)
|| repaymentPeriod.getDueDate().isEqual(reageFirstDueDate))
&& !repaymentPeriod.getDueDate().isAfter(newPeriods.getLast().getDueDate()))
.filter(repaymentPeriod -> (repaymentPeriod.getFromDate().isEqual(targetDate)
&& repaymentPeriod.getDueDate().isEqual(reAgeFirstDueDate))
|| !repaymentPeriod.getFromDate().isBefore(reAgeFirstDueDate))
.toList();
// cleanup repayment periods after re-aging
if (reAgedRepaymentPeriods.getLast() != null) {
final List<RepaymentPeriod> repaymentPeriodsToRemove = existingRepaymentPeriods.stream()
.filter(rp -> !rp.getFromDate().isBefore(reAgedRepaymentPeriods.getLast().getDueDate())).toList();
repaymentPeriodsToRemove.forEach(existingRepaymentPeriods::remove);
}
calculateOutstandingBalance(scheduleModel);
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, targetDate);
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, reAgedRepaymentPeriods);
@@ -773,13 +780,20 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* principal that was moved.
*/
private static void moveOutstandingAmountsFromPeriodsBeforeReAging(final List<RepaymentPeriod> existingRepaymentPeriods,
final LocalDate reAgingStartDate) {
final LocalDate transactionDate) {
final List<RepaymentPeriod> periodsBeforeReAging = existingRepaymentPeriods.stream()
.filter(rp -> rp.getFromDate().isBefore(reAgingStartDate) && !rp.isFullyPaid()).toList();
.filter(rp -> !rp.getFromDate().isAfter(transactionDate) && !rp.isFullyPaid()).toList();
periodsBeforeReAging.forEach(rp -> {
rp.getInterestPeriods().stream().filter(ip -> ip.getDueDate().isEqual(transactionDate)).forEach(ip -> {
if (!ip.getBalanceCorrectionAmount().isZero()) {
ip.addBalanceCorrectionAmount(ip.getBalanceCorrectionAmount().negated());
}
});
final InterestPeriod lastInterestPeriod = rp.getInterestPeriods().getLast();
lastInterestPeriod.addBalanceCorrectionAmount(rp.getOutstandingPrincipal().negated());
if (!lastInterestPeriod.getBalanceCorrectionAmount().isZero()) {
lastInterestPeriod.addBalanceCorrectionAmount(rp.getOutstandingPrincipal().negated());
}
rp.setEmi(rp.getTotalPaidAmount());
rp.setOutstandingMovedDueToReAging(true);
});
@@ -876,10 +890,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
if (newScheduleModel == null) {
newScheduleModel = scheduleModel.deepCopy(mc);
}
final LocalDate relatedPeriodsFirstFromDate = relatedRepaymentPeriods.getFirst().getFromDate();
final LocalDate relatedPeriodsFirstDueDate = relatedRepaymentPeriods.getFirst().getDueDate();
newScheduleModel.repaymentPeriods().forEach(period -> {
if (!period.getDueDate().isBefore(relatedPeriodsFirstDueDate)
&& !adjustedEqualMonthlyInstallmentValue.isLessThan(period.getTotalPaidAmount())) {
if (!period.getFromDate().isBefore(relatedPeriodsFirstFromDate) && !period.getDueDate().isBefore(relatedPeriodsFirstDueDate)
&& !adjustedEqualMonthlyInstallmentValue.isLessThan(period.getTotalPaidAmount())
&& !period.isReAgedEarlyRepaymentHolder()) {
period.setEmi(adjustedEqualMonthlyInstallmentValue);
period.setOriginalEmi(adjustedEqualMonthlyInstallmentValue);
}
@@ -891,7 +907,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}
final Iterator<RepaymentPeriod> relatedPeriodFromNewModelIterator = newScheduleModel.repaymentPeriods().stream()//
.filter(period -> !period.getDueDate().isBefore(relatedPeriodsFirstDueDate))//
.filter(period -> !period.getFromDate().isBefore(relatedPeriodsFirstFromDate)
&& !period.getDueDate().isBefore(relatedPeriodsFirstDueDate) && !period.isReAgedEarlyRepaymentHolder())//
.toList().iterator();//
relatedRepaymentPeriods.forEach(relatedRepaymentPeriod -> {
@@ -1071,7 +1088,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
calculatedDate = seedDate.plus(multiplicator, chronoUnit);
multiplicator++;
} while (calculatedDate.isBefore(repaymentPeriod.getDueDate()));
return calculatedDate.equals(repaymentPeriod.getDueDate()) ? seedDate : repaymentPeriod.getFromDate();
return calculatedDate.equals(repaymentPeriod.getDueDate()) && calculatedDate
.minus(scheduleModel.loanProductRelatedDetail().getRepayEvery(), chronoUnit).isEqual(repaymentPeriod.getFromDate())
? seedDate
: repaymentPeriod.getFromDate();
}
/**
@@ -1563,8 +1583,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*/
@Override
public Money getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate subjectDate) {
return scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueDate) //
.map(repaymentPeriodDueDate -> getDueAmounts(scheduleModel, repaymentPeriodDueDate, subjectDate) //
return scheduleModel.repaymentPeriods().stream() //
.map(repaymentPeriod -> getDueAmounts(scheduleModel, repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate(),
subjectDate) //
.getDueInterest()) //
.reduce(scheduleModel.zero(), Money::add); //
}
@@ -1663,7 +1684,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
if (!paidBalancesFromTransactionDate.getOutstandingInterest().isZero()
|| !paidBalancesFromTransactionDate.getOutstandingPrincipal().isZero()) {
createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(interestSchedule,
paidBalancesFromTransactionDate.getOutstandingPrincipal(), paidBalancesFromTransactionDate.getOutstandingInterest());
paidBalancesFromTransactionDate.getOutstandingPrincipal(), paidBalancesFromTransactionDate.getOutstandingInterest(),
true);
addFirstReAgedPeriod(interestSchedule, interestSchedule.getLastRepaymentPeriod());
}
updateModelForReageEqualAmortization(interestSchedule, reageParameter, reAgedRepaymentPeriods, isAfterOriginalMaturityDate);
@@ -1717,18 +1740,23 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}
}
private void createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(ProgressiveLoanInterestScheduleModel interestSchedule,
Money totalPaidPrincipal, Money totalPaidInterest) {
RepaymentPeriod targetPeriod = interestSchedule.getLastRepaymentPeriod();
private void createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(final ProgressiveLoanInterestScheduleModel interestSchedule,
final Money totalPaidPrincipal, final Money totalPaidInterest, final boolean isReAged) {
final RepaymentPeriod targetPeriod = interestSchedule.getLastRepaymentPeriod();
Money paidInterestToAdd = totalPaidInterest.minus(targetPeriod.getPaidInterest());
Money paidPrincipalToAdd = totalPaidPrincipal.minus(targetPeriod.getPaidPrincipal());
final Money paidInterestToAdd = totalPaidInterest.minus(targetPeriod.getPaidInterest());
final Money paidPrincipalToAdd = totalPaidPrincipal.minus(targetPeriod.getPaidPrincipal());
targetPeriod.addPaidInterestAmount(paidInterestToAdd);
targetPeriod.addPaidPrincipalAmount(paidPrincipalToAdd);
targetPeriod.setEmi(targetPeriod.getTotalPaidAmount());
targetPeriod.setReAged(true);
if (isReAged) {
targetPeriod.setReAged(true);
}
targetPeriod.setReAgedEarlyRepaymentHolder(true);
calculateRateFactorForRepaymentPeriod(targetPeriod, interestSchedule);
}
private static void addFirstReAgedPeriod(ProgressiveLoanInterestScheduleModel interestSchedule, RepaymentPeriod targetPeriod) {
RepaymentPeriod repaymentPeriodToInsert = RepaymentPeriod.create(targetPeriod, targetPeriod.getDueDate(),
interestSchedule.getMaturityDate(), interestSchedule.zero(), interestSchedule.mc(),
interestSchedule.loanProductRelatedDetail());
@@ -143,12 +143,14 @@ public class ProgressiveLoanInterestScheduleModel {
interestRates.add(new InterestRate(newInterestEffectiveDate, newInterestRate));
}
public Optional<RepaymentPeriod> findRepaymentPeriodByDueDate(final LocalDate repaymentPeriodDueDate) {
public Optional<RepaymentPeriod> findRepaymentPeriodByFromAndDueDate(final LocalDate repaymentPeriodFromDate,
final LocalDate repaymentPeriodDueDate) {
if (repaymentPeriodDueDate == null) {
return Optional.empty();
}
return repaymentPeriods.stream()//
.filter(repaymentPeriodItem -> DateUtils.isEqual(repaymentPeriodItem.getDueDate(), repaymentPeriodDueDate))//
.filter(repaymentPeriodItem -> DateUtils.isEqual(repaymentPeriodItem.getFromDate(), repaymentPeriodFromDate)
&& DateUtils.isEqual(repaymentPeriodItem.getDueDate(), repaymentPeriodDueDate))//
.findFirst();
}
@@ -507,9 +507,10 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
ArgumentCaptor<Money> paidPortionCaptor = ArgumentCaptor.forClass(Money.class);
ArgumentCaptor<LocalDate> payDateCaptor = ArgumentCaptor.forClass(LocalDate.class);
final LocalDate repaymentPeriodFromDate = transactionDate.minusMonths(1);
// Verify `payPrincipal` is called when allocation type is DUE_PRINCIPAL
Mockito.verify(emiCalculator, atLeastOnce()).payPrincipal(eq(model), eq(transactionDate), payDateCaptor.capture(),
paidPortionCaptor.capture());
Mockito.verify(emiCalculator, atLeastOnce()).payPrincipal(eq(model), eq(repaymentPeriodFromDate), eq(transactionDate),
payDateCaptor.capture(), paidPortionCaptor.capture());
Money paidPortion = paidPortionCaptor.getValue();
LocalDate payDate = payDateCaptor.getValue();
@@ -447,10 +447,13 @@ class ProgressiveEMICalculatorTest {
final Money disbursedAmount = toMoney(100.0);
emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount);
emiCalculator.payInterest(interestModel, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(0.58));
emiCalculator.payPrincipal(interestModel, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43));
emiCalculator.payInterest(interestModel, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(0.58));
emiCalculator.payPrincipal(interestModel, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(16.43));
emiCalculator.payPrincipal(interestModel, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1), toMoney(16.90));
emiCalculator.payPrincipal(interestModel, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
toMoney(16.90));
final BigDecimal interestRateNewValue = BigDecimal.valueOf(4.0);
final LocalDate interestChangeDate = LocalDate.of(2024, 2, 2);
@@ -495,7 +498,8 @@ class ProgressiveEMICalculatorTest {
final Money disbursedAmount = toMoney(100.0);
emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount);
emiCalculator.payPrincipal(interestModel, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestModel, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 15),
toMoney(17.01));
final BigDecimal interestRateNewValue = BigDecimal.valueOf(4.0);
final LocalDate interestChangeDate = LocalDate.of(2024, 1, 20);
@@ -588,12 +592,14 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// schedule 1st period 1st day
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 1));
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1),
LocalDate.of(2024, 1, 1));
Assertions.assertEquals(17.01, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.0, toDouble(dueAmounts.getDueInterest()));
// schedule 2nd period last day
dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1));
dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1),
LocalDate.of(2024, 3, 1));
Assertions.assertEquals(16.52, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.49, toDouble(dueAmounts.getDueInterest()));
@@ -603,13 +609,15 @@ class ProgressiveEMICalculatorTest {
final Money op1stCorrectionAmount = toMoney(16.77);
// get remaining balance and dues for a date
final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, op1stCorrectionPeriodDueDate,
op1stCorrectionDate);
final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1),
op1stCorrectionPeriodDueDate, op1stCorrectionDate);
Assertions.assertEquals(16.77, toDouble(repaymentDetails1st.getDuePrincipal()));
Assertions.assertEquals(0.24, toDouble(repaymentDetails1st.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, op1stCorrectionPeriodDueDate, op1stCorrectionDate, op1stCorrectionAmount);
emiCalculator.payInterest(interestSchedule, op1stCorrectionPeriodDueDate, op1stCorrectionDate, toMoney(0.24));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), op1stCorrectionPeriodDueDate, op1stCorrectionDate,
op1stCorrectionAmount);
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), op1stCorrectionPeriodDueDate, op1stCorrectionDate,
toMoney(0.24));
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
checkPeriod(interestSchedule, 1, 0, 17.01, 0.002816091954, 0.235340804584, 0.24, 16.77, 66.80);
@@ -625,13 +633,15 @@ class ProgressiveEMICalculatorTest {
final Money op2ndCorrectionAmount = toMoney(16.42);
// get remaining balance and dues for a date
final PeriodDueDetails repaymentDetails2st = emiCalculator.getDueAmounts(interestSchedule, op2ndCorrectionPeriodDueDate,
op2ndCorrectionDate);
final PeriodDueDetails repaymentDetails2st = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1),
op2ndCorrectionPeriodDueDate, op2ndCorrectionDate);
Assertions.assertEquals(16.81, toDouble(repaymentDetails2st.getDuePrincipal()));
Assertions.assertEquals(0.20, toDouble(repaymentDetails2st.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, op2ndCorrectionPeriodDueDate, op2ndCorrectionDate, op2ndCorrectionAmount);
emiCalculator.payInterest(interestSchedule, op2ndCorrectionPeriodDueDate, op2ndCorrectionDate, toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), op2ndCorrectionPeriodDueDate, op2ndCorrectionDate,
op2ndCorrectionAmount);
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), op2ndCorrectionPeriodDueDate, op2ndCorrectionDate,
toMoney(0.49));
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
checkPeriod(interestSchedule, 1, 0, 17.01, 0.002816091954, 0.235340804584, 0.24, 16.77, 50.38);
@@ -644,14 +654,16 @@ class ProgressiveEMICalculatorTest {
// check numbers on last period due date
LocalDate periodDueDate = LocalDate.of(2024, 7, 1);
LocalDate payDate = LocalDate.of(2024, 7, 1);
final PeriodDueDetails repaymentDetails3rd = emiCalculator.getDueAmounts(interestSchedule, periodDueDate, payDate);
final PeriodDueDetails repaymentDetails3rd = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 6, 1), periodDueDate,
payDate);
Assertions.assertEquals(16.75, toDouble(repaymentDetails3rd.getDuePrincipal()));
Assertions.assertEquals(0.1, toDouble(repaymentDetails3rd.getDueInterest()));
// check numbers after the last period due date
periodDueDate = LocalDate.of(2024, 7, 1);
payDate = LocalDate.of(2024, 7, 15);
final PeriodDueDetails repaymentDetails4th = emiCalculator.getDueAmounts(interestSchedule, periodDueDate, payDate);
final PeriodDueDetails repaymentDetails4th = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 6, 1), periodDueDate,
payDate);
Assertions.assertEquals(16.75, toDouble(repaymentDetails4th.getDuePrincipal()));
Assertions.assertEquals(0.1, toDouble(repaymentDetails4th.getDueInterest()));
@@ -700,30 +712,41 @@ class ProgressiveEMICalculatorTest {
final Money op1stCorrectionAmount = toMoney(15.0);
// get remaining balance and dues for a date
final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, op1stCorrectionPeriodDueDate,
op1stCorrectionDate);
final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1),
op1stCorrectionPeriodDueDate, op1stCorrectionDate);
Assertions.assertEquals(16.77, toDouble(repaymentDetails1st.getDuePrincipal()));
Assertions.assertEquals(0.24, toDouble(repaymentDetails1st.getDueInterest()));
// check getDueAmounts forcast
PeriodDueDetails details = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1));
PeriodDueDetails details = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1),
LocalDate.of(2024, 3, 1));
Assertions.assertEquals(16.52, toDouble(details.getDuePrincipal()));
Assertions.assertEquals(0.49, toDouble(details.getDueInterest()));
// apply balance change and check again
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), op1stCorrectionDate, op1stCorrectionAmount);
details = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), op1stCorrectionDate,
op1stCorrectionAmount);
details = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1),
LocalDate.of(2024, 3, 1));
Assertions.assertEquals(16.52, toDouble(details.getDuePrincipal()));
Assertions.assertEquals(0.49, toDouble(details.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15), toMoney(1.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15), toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15), toMoney(16.77));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15), toMoney(0.24));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 2, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 2, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 15), toMoney(15.77));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15),
toMoney(1.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15),
toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15),
toMoney(16.77));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15),
toMoney(0.24));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 2, 15),
toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1), LocalDate.of(2024, 2, 15),
toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 15),
toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 15),
toMoney(15.77));
// check periods in model
@@ -766,28 +789,37 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// get remaining balance and dues on due date
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1));
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1),
LocalDate.of(2024, 2, 1));
Assertions.assertEquals(16.43, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.58, toDouble(dueAmounts.getDueInterest()));
// check numbers on payoff date
dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15));
dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1),
LocalDate.of(2024, 1, 15));
Assertions.assertEquals(16.75, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.26, toDouble(dueAmounts.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15), toMoney(16.75));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15), toMoney(0.26));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15),
toMoney(16.75));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15),
toMoney(0.26));
// check again numbers are zero
// dueAmounts = emiCalculator.getdueAmounts(interestSchedule, LocalDate.of(2024, 2, 1),
// LocalDate.of(2024, 2, 1));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 1, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 1, 15), toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 15),
toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 15),
toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1), LocalDate.of(2024, 1, 15),
toMoney(17.01));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 1, 15),
toMoney(17.01));
dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 7, 1));
dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1),
LocalDate.of(2024, 7, 1));
Assertions.assertEquals(15.21, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.5, toDouble(dueAmounts.getDueInterest()));
@@ -801,7 +833,8 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.0887249999949, 0.0, 17.01, 15.21);
checkPeriod(interestSchedule, 5, 0, 15.71, 0.005833333333, 0.0887249999949, 0.5, 15.21, 0.0);
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 15), toMoney(15.21));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 15),
toMoney(15.21));
// check periods in model
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.26, 16.75, 0.0);
@@ -1023,12 +1056,18 @@ class ProgressiveEMICalculatorTest {
Assertions.assertEquals(6.15, toDouble(interestSchedule.getTotalDueInterest()));
// pay back the whole loan on first day
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 1), toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 1), toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 1), toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 1, 1), toMoney(51.03));
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));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 1),
toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 1),
toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 1),
toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1), LocalDate.of(2024, 1, 1),
toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 1, 1),
toMoney(51.03));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 1),
toMoney(44.85));
checkTotalInterestDue(interestSchedule, 0.0);
@@ -1208,11 +1247,12 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
for (int i = 0; i < 12; i++) {
LocalDate fromDate = expectedRepaymentPeriods.get(i).getFromDate();
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate();
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, dueDate, disbursementDate);
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, fromDate, dueDate, disbursementDate);
Money duePrincipal = dueAmounts.getDuePrincipal();
if (duePrincipal.isGreaterThanZero()) {
emiCalculator.payPrincipal(interestSchedule, dueDate, disbursementDate, duePrincipal);
emiCalculator.payPrincipal(interestSchedule, fromDate, dueDate, disbursementDate, duePrincipal);
}
}
@@ -1321,7 +1361,7 @@ class ProgressiveEMICalculatorTest {
final LocalDate dueDate = LocalDate.of(2024, 2, 1);
final LocalDate startDay = LocalDate.of(2024, 1, 1);
emiCalculator.payInterest(interestModel, dueDate, startDay.plusDays(3), toMoney(0.56));
emiCalculator.payInterest(interestModel, startDay, dueDate, startDay.plusDays(3), toMoney(0.56));
emiCalculator.creditInterest(interestModel, startDay.plusDays(3), toMoney(0.0));
emiCalculator.addBalanceCorrection(interestModel, startDay.plusDays(3), toMoney(0.0));
@@ -2019,9 +2059,9 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 4, 857.71, 14.33, 843.38, 850.76, false);
checkPeriod(interestSchedule, 5, 857.73, 6.97, 850.76, 0, false);
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2023, 11, 13), LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
Money.of(currency, BigDecimal.valueOf(820.76)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2023, 11, 13), LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
Money.of(currency, BigDecimal.valueOf(36.95)));
checkPeriod(interestSchedule, 0, 857.71, 36.95, 820.76, 4179.24, true);
@@ -2062,8 +2102,9 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
for (int i = 0; i < 6; i++) {
LocalDate fromDate = expectedRepaymentPeriods.get(i).getFromDate();
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate();
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, dueDate, dueDate);
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, fromDate, dueDate, dueDate);
Money duePrincipal = dueAmounts.getDuePrincipal();
emiCalculator.addBalanceCorrection(interestSchedule, dueDate, duePrincipal);
}
@@ -2105,14 +2146,15 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
emiCalculator.payInterest(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2023, 11, 13), LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
Money.of(currency, BigDecimal.valueOf(36.95)));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2023, 11, 13), LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
Money.of(currency, BigDecimal.valueOf(820.76)));
for (int i = 1; i < 6; i++) {
LocalDate fromDate = expectedRepaymentPeriods.get(i).getFromDate();
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate();
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, dueDate, dueDate);
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, fromDate, dueDate, dueDate);
Money duePrincipal = dueAmounts.getDuePrincipal();
emiCalculator.addBalanceCorrection(interestSchedule, dueDate, duePrincipal);
}
@@ -2450,11 +2492,15 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.00, 0.005833333333, 0.0985833333276, 0.1, 16.9, 0.0);
// repay 1st period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(0.58));
// repay 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
toMoney(0.49));
// full chargeback on duedate
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(17.01));
@@ -2503,11 +2549,15 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.00, 0.005833333333, 0.0985833333276, 0.1, 16.9, 0.0);
// repay 1st period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(0.58));
// repay 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
toMoney(0.49));
// partial chargeback
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(15.0));
@@ -2567,11 +2617,15 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.00, 0.005833333333, 0.0985833333276, 0.1, 16.9, 0.0);
// repay 1st period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
toMoney(0.58));
// repay 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
toMoney(0.49));
// full chargeback on duedate
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 15), toMoney(17.01));
@@ -2623,13 +2677,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period
txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback
txnDate = LocalDate.of(2024, 3, 1);
@@ -2681,13 +2735,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period
txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback
txnDate = LocalDate.of(2024, 7, 1);
@@ -2733,13 +2787,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period
txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback
txnDate = LocalDate.of(2024, 3, 1);
@@ -2791,13 +2845,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period
txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback
txnDate = LocalDate.of(2024, 3, 1);
@@ -2848,13 +2902,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period
txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback 1st
txnDate = LocalDate.of(2024, 3, 1);
@@ -3142,20 +3196,21 @@ class ProgressiveEMICalculatorTest {
// repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period
txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback
txnDate = LocalDate.of(2024, 3, 15);
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(16.52));
emiCalculator.creditInterest(interestSchedule, txnDate, toMoney(0.49));
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), txnDate);
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1),
txnDate);
Assertions.assertEquals(33.09, toDouble(interestSchedule.repaymentPeriods().get(2).getDuePrincipal()));
Assertions.assertEquals(33.35, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.67, toDouble(dueAmounts.getDueInterest()));
@@ -3204,10 +3259,10 @@ class ProgressiveEMICalculatorTest {
// repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestScheduleExpected, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestScheduleExpected, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestScheduleActual, txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestScheduleActual, txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestScheduleExpected, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestScheduleExpected, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestScheduleActual, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestScheduleActual, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
verifyAllPeriods(interestScheduleExpected, interestScheduleActual);
interestScheduleActual = copyJson(interestScheduleExpected);
@@ -3215,10 +3270,10 @@ class ProgressiveEMICalculatorTest {
// repay 2nd period
txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestScheduleExpected, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestScheduleExpected, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestScheduleActual, txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestScheduleActual, txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestScheduleExpected, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestScheduleExpected, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestScheduleActual, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestScheduleActual, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
verifyAllPeriods(interestScheduleExpected, interestScheduleActual);
interestScheduleActual = copyJson(interestScheduleExpected);
@@ -3235,8 +3290,10 @@ class ProgressiveEMICalculatorTest {
interestScheduleActual = copyJson(interestScheduleExpected);
verifyAllPeriods(interestScheduleExpected, interestScheduleActual);
PeriodDueDetails dueAmountsExpected = emiCalculator.getDueAmounts(interestScheduleExpected, LocalDate.of(2024, 4, 1), txnDate);
PeriodDueDetails dueAmountsActual = emiCalculator.getDueAmounts(interestScheduleActual, LocalDate.of(2024, 4, 1), txnDate);
PeriodDueDetails dueAmountsExpected = emiCalculator.getDueAmounts(interestScheduleExpected, LocalDate.of(2024, 3, 1),
LocalDate.of(2024, 4, 1), txnDate);
PeriodDueDetails dueAmountsActual = emiCalculator.getDueAmounts(interestScheduleActual, LocalDate.of(2024, 3, 1),
LocalDate.of(2024, 4, 1), txnDate);
Assertions.assertEquals(toDouble(dueAmountsExpected.getDuePrincipal()), toDouble(dueAmountsActual.getDuePrincipal()));
Assertions.assertEquals(toDouble(dueAmountsExpected.getDueInterest()), toDouble(dueAmountsActual.getDueInterest()));
@@ -4069,11 +4126,11 @@ class ProgressiveEMICalculatorTest {
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),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 9, 1), 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),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), 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
@@ -4130,9 +4187,9 @@ class ProgressiveEMICalculatorTest {
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),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 9, 1), 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),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 9, 1), 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); //
@@ -4141,9 +4198,9 @@ class ProgressiveEMICalculatorTest {
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),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), 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),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 6, 1), 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
@@ -4283,26 +4340,30 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(16.1)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(2.57)));
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 1),
emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal());
emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1))
.getDuePrincipal());
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15), emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal().negated());
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15),
emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1))
.getDuePrincipal().negated());
LocalDate reAgingStartDate = LocalDate.of(2024, 4, 20);
@@ -4360,9 +4421,9 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// repay EMI on first period due date
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(16.43)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(0.58)));
// chargeback
@@ -4419,9 +4480,9 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// repay EMI on first period due date
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(16.43)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(0.58)));
// chargeback
@@ -4487,26 +4548,30 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(16.1)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(2.57)));
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 1),
emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal());
emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1))
.getDuePrincipal());
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15), emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal().negated());
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15),
emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1))
.getDuePrincipal().negated());
LocalDate transactionDate = LocalDate.of(2024, 4, 15);
LocalDate reAgingStartDate = LocalDate.of(2024, 4, 20);
@@ -4566,26 +4631,30 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(16.1)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(2.57)));
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 1),
emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal());
emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1))
.getDuePrincipal());
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15), emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal().negated());
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15),
emiCalculator
.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1))
.getDuePrincipal().negated());
LocalDate transactionDate = LocalDate.of(2024, 4, 15);
LocalDate reAgingStartDate = LocalDate.of(2024, 5, 1);
@@ -4645,19 +4714,19 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(16.1)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(2.57)));
LocalDate transactionDate = LocalDate.of(2024, 7, 1);
@@ -4718,19 +4787,19 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(16.1)));
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(17.43)));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 1),
Money.of(currency, BigDecimal.valueOf(2.57)));
LocalDate transactionDate = LocalDate.of(2024, 7, 10);
@@ -4809,9 +4878,9 @@ class ProgressiveEMICalculatorTest {
private static void checkDailyInterest(final ProgressiveLoanInterestScheduleModel interestModel, final LocalDate repaymentPeriodDueDate,
final LocalDate interestStartDay, final int dayOffset, final double dailyInterest, final double interest) {
Money previousInterest = emiCalculator.getPeriodInterestTillDate(interestModel, repaymentPeriodDueDate,
Money previousInterest = emiCalculator.getPeriodInterestTillDate(interestModel, interestStartDay, repaymentPeriodDueDate,
interestStartDay.plusDays(dayOffset - 1), true);
Money currentInterest = emiCalculator.getPeriodInterestTillDate(interestModel, repaymentPeriodDueDate,
Money currentInterest = emiCalculator.getPeriodInterestTillDate(interestModel, interestStartDay, repaymentPeriodDueDate,
interestStartDay.plusDays(dayOffset), true);
Assertions.assertEquals(dailyInterest, toDouble(currentInterest.minus(previousInterest)));
Assertions.assertEquals(interest, toDouble(currentInterest));