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: When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 | | 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 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 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 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 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 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 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 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 | | 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: Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 0.0 | 0.0 | 102.8 | 17.01 | 0.0 | 0.0 | 85.79 | | 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: When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 | | 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 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 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 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 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 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 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 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 | | 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: Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 10.0 | 0.0 | 112.8 | 17.01 | 0.0 | 0.0 | 95.79 | | 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: When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 | | 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 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 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 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 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 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 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 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 | | 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: Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 10.0 | 0.0 | 112.8 | 17.01 | 0.0 | 0.0 | 95.79 | | 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: When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024| 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 50.97 | 12.56 | 0.37 | 0.0 | 0.0 | 12.93 | 0.0 | 0.0 | 0.0 | 12.93 | | 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 | 31 | 01 June 2024 | | 38.34 | 12.63 | 0.3 | 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 | 30 | 01 July 2024 | | 25.63 | 12.71 | 0.22 | 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 | 31 | 01 August 2024 | | 12.85 | 12.78 | 0.15 | 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 September 2024| | 0.0 | 12.85 | 0.07 | 0.0 | 0.0 | 12.92 | 0.0 | 0.0 | 0.0 | 12.92 | | 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: Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.57 | 0.0 | 0.0 | 102.57 | 25.0 | 7.99 | 0.0 | 77.57 | | 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: When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 42.27 | 10.42 | 0.31 | 0.0 | 0.0 | 10.73 | 0.0 | 0.0 | 0.0 | 10.73 | | 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 | 31 | 01 June 2024 | | 31.79 | 10.48 | 0.25 | 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 | 30 | 01 July 2024 | | 21.25 | 10.54 | 0.19 | 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 | 31 | 01 August 2024 | | 10.64 | 10.61 | 0.12 | 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 September 2024 | | 0.0 | 10.64 | 0.06 | 0.0 | 0.0 | 10.7 | 0.0 | 0.0 | 0.0 | 10.7 | | 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: Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.11 | 0.0 | 0.0 | 102.11 | 37.76 | 0.0 | 0.0 | 64.35 | | 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: When Admin creates a Loan re-aging transaction with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 | | 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 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 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 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 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 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 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 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 | | 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: Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 0.0 | 0.0 | 102.8 | 17.01 | 0.0 | 0.0 | 85.79 | | 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: When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 | | 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 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 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 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 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 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 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 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 | | 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: Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 0.0 | 0.0 | 102.8 | 17.01 | 0.0 | 0.0 | 85.79 | | 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: When Admin creates a Loan re-aging preview by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments | | frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 April 2024 | 6 | | 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 | | 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 | | | | | | | 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 | | 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 | | 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 | | 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 | 30 | 01 May 2024 | | 56.36 | 13.89 | 0.41 | 0.0 | 0.0 | 14.3 | 0.0 | 0.0 | 0.0 | 14.3 | | 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 | 31 | 01 June 2024 | | 42.39 | 13.97 | 0.33 | 10.0 | 0.0 | 24.3 | 0.0 | 0.0 | 0.0 | 24.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 | 30 | 01 July 2024 | | 28.34 | 14.05 | 0.25 | 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 | 31 | 01 August 2024 | | 14.21 | 14.13 | 0.17 | 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 September 2024| | 0.0 | 14.21 | 0.08 | 0.0 | 0.0 | 14.29 | 0.0 | 0.0 | 0.0 | 14.29 | | 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: Then Loan Repayment schedule preview has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.8 | 10.0 | 0.0 | 112.8 | 17.01 | 0.0 | 0.0 | 95.79 | | 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); 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) { public static LoanRepaymentScheduleInstallment getLastNonDownPaymentInstallment(List<LoanRepaymentScheduleInstallment> installments) {
return installments.stream().filter(i -> !i.isDownPayment()).reduce((first, second) -> second).orElseThrow(); 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.setWrittenOff(loan.isClosedWrittenOff());
ctx.setContractTerminated(loan.isContractTermination()); ctx.setContractTerminated(loan.isContractTermination());
advancedPaymentScheduleTransactionProcessor.recalculateInterestForDate(nextPaymentDueDate, ctx, false); advancedPaymentScheduleTransactionProcessor.recalculateInterestForDate(nextPaymentDueDate, ctx, false);
RepaymentPeriod repaymentPeriod = scheduleModel.findRepaymentPeriodByDueDate(nextPaymentDueDate) RepaymentPeriod repaymentPeriod = scheduleModel
.findRepaymentPeriodByFromAndDueDate(nextInstallment.getFromDate(), nextInstallment.getDueDate())
.orElseGet(scheduleModel::getLastRepaymentPeriod); .orElseGet(scheduleModel::getLastRepaymentPeriod);
return repaymentPeriod.getOutstandingPrincipal().add(repaymentPeriod.getOutstandingInterest()) return repaymentPeriod.getOutstandingPrincipal().add(repaymentPeriod.getOutstandingInterest())
@@ -45,10 +45,10 @@ import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@@ -365,10 +365,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateInstallmentIfInterestPeriodPresent(final ProgressiveLoanInterestScheduleModel scheduleModel, private void updateInstallmentIfInterestPeriodPresent(final ProgressiveLoanInterestScheduleModel scheduleModel,
final LoanRepaymentScheduleInstallment installment) { final LoanRepaymentScheduleInstallment installment) {
emiCalculator.findRepaymentPeriod(scheduleModel, installment.getDueDate()).ifPresent(interestRepaymentPeriod -> { emiCalculator.findRepaymentPeriod(scheduleModel, installment.getFromDate(), installment.getDueDate())
installment.updateInterestCharged(interestRepaymentPeriod.getDueInterest().getAmount()); .ifPresent(interestRepaymentPeriod -> {
installment.updatePrincipal(interestRepaymentPeriod.getDuePrincipal().getAmount()); installment.updateInterestCharged(interestRepaymentPeriod.getDueInterest().getAmount());
}); installment.updatePrincipal(interestRepaymentPeriod.getDuePrincipal().getAmount());
});
} }
@Override @Override
@@ -592,7 +593,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx); updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
if (repaidAmount.isGreaterThanZero()) { if (repaidAmount.isGreaterThanZero()) {
emiCalculator.payPrincipal(model, currentInstallment.getDueDate(), transactionDate, repaidAmount); emiCalculator.payPrincipal(model, currentInstallment.getFromDate(), currentInstallment.getDueDate(),
transactionDate, repaidAmount);
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx); updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
currentInstallment.payPrincipalComponent(transactionDate, repaidAmount); currentInstallment.payPrincipalComponent(transactionDate, repaidAmount);
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
@@ -629,7 +631,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx); updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
if (repaidAmount.isGreaterThanZero()) { if (repaidAmount.isGreaterThanZero()) {
emiCalculator.payPrincipal(model, currentInstallment.getDueDate(), transactionDate, repaidAmount); emiCalculator.payPrincipal(model, currentInstallment.getFromDate(), currentInstallment.getDueDate(),
transactionDate, repaidAmount);
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx); updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
currentInstallment.payPrincipalComponent(transactionDate, repaidAmount); currentInstallment.payPrincipalComponent(transactionDate, repaidAmount);
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
@@ -1581,7 +1584,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateInstallmentsPrincipalAndInterestByModel(ProgressiveTransactionCtx ctx) { private void updateInstallmentsPrincipalAndInterestByModel(ProgressiveTransactionCtx ctx) {
ctx.getModel().repaymentPeriods().forEach(repayment -> { ctx.getModel().repaymentPeriods().forEach(repayment -> {
LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream() 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); .findFirst().orElse(null);
if (installment != null) { if (installment != null) {
installment.updatePrincipal(repayment.getDuePrincipal().getAmount()); installment.updatePrincipal(repayment.getDuePrincipal().getAmount());
@@ -1774,7 +1778,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
&& loan.isInterestBearingAndInterestRecalculationEnabled()) { && loan.isInterestBearingAndInterestRecalculationEnabled()) {
final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount(); final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), 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) { if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) {
currentInstallment.updateInterestCharged(newInterest); currentInstallment.updateInterestCharged(newInterest);
} }
@@ -1887,7 +1891,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
&& installment.getDueDate().isAfter(transactionDate)).forEach(installment -> { && installment.getDueDate().isAfter(transactionDate)).forEach(installment -> {
final BigDecimal interestOutstanding = installment.getInterestOutstanding(currency).getAmount(); final BigDecimal interestOutstanding = installment.getInterestOutstanding(currency).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), 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)) { if (MathUtil.isGreaterThanZero(interestOutstanding) || MathUtil.isGreaterThanZero(newInterest)) {
final BigDecimal interestRemoved = MathUtil.subtract(MathUtil.nullToZero(installment.getInterestCharged()), final BigDecimal interestRemoved = MathUtil.subtract(MathUtil.nullToZero(installment.getInterestCharged()),
newInterest); newInterest);
@@ -2503,10 +2507,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
loanTransactionToRepaymentScheduleMapping, charges, balances, LoanRepaymentScheduleInstallment.PaymentAction.PAY); loanTransactionToRepaymentScheduleMapping, charges, balances, LoanRepaymentScheduleInstallment.PaymentAction.PAY);
if (PRINCIPAL.equals(paymentAllocationType.getAllocationType())) { if (PRINCIPAL.equals(paymentAllocationType.getAllocationType())) {
emiCalculator.payPrincipal(model, installment.getDueDate(), payDate, paidPortion); emiCalculator.payPrincipal(model, installment.getFromDate(), installment.getDueDate(), payDate, paidPortion);
updateRepaymentPeriods(loanTransaction, ctx); updateRepaymentPeriods(loanTransaction, ctx);
} else if (INTEREST.equals(paymentAllocationType.getAllocationType())) { } 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); updateRepaymentPeriods(loanTransaction, ctx);
} }
return paidPortion; return paidPortion;
@@ -2514,8 +2518,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateRepaymentPeriods(LoanTransaction loanTransaction, ProgressiveTransactionCtx ctx) { private void updateRepaymentPeriods(LoanTransaction loanTransaction, ProgressiveTransactionCtx ctx) {
ctx.getModel().repaymentPeriods().forEach(rm -> { ctx.getModel().repaymentPeriods().forEach(rm -> {
LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream() LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream().filter(
.filter(ri -> ri.getDueDate().equals(rm.getDueDate()) && !ri.isDownPayment()).findFirst().orElse(null); ri -> ri.getFromDate().equals(rm.getFromDate()) && ri.getDueDate().equals(rm.getDueDate()) && !ri.isDownPayment())
.findFirst().orElse(null);
if (installment != null) { if (installment != null) {
installment.updatePrincipal(rm.getDuePrincipal().getAmount()); installment.updatePrincipal(rm.getDuePrincipal().getAmount());
installment.updateInterestCharged(rm.getDueInterest().getAmount()); installment.updateInterestCharged(rm.getDueInterest().getAmount());
@@ -2528,7 +2533,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateRepaymentPeriodBalances(PaymentAllocationType paymentAllocationType, private void updateRepaymentPeriodBalances(PaymentAllocationType paymentAllocationType,
LoanRepaymentScheduleInstallment inAdvanceInstallment, ProgressiveTransactionCtx ctx, LocalDate payDate) { 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()) { if (payableDetails.getDueInterest().isZero() && payableDetails.getDuePrincipal().isZero()) {
ctx.getSkipRepaymentScheduleInstallments().add(inAdvanceInstallment); ctx.getSkipRepaymentScheduleInstallments().add(inAdvanceInstallment);
@@ -2777,16 +2783,17 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private Money fee; private Money fee;
private Money penalty; private Money penalty;
private Money paidInAdvance; private Money paidInAdvance;
private BigDecimal paidLate;
private Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings; private Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings;
private BalancesWithPaidInAdvance(MonetaryCurrency currency) { private BalancesWithPaidInAdvance(MonetaryCurrency currency) {
this(Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(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) { private BalancesWithPaidInAdvance(LoanRepaymentScheduleInstallment i, MonetaryCurrency currency) {
this(i.getPrincipalCompleted(currency), i.getInterestPaid(currency), i.getFeeChargesPaid(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())); new LinkedHashSet<>(i.getLoanTransactionToRepaymentScheduleMappings()));
} }
@@ -2796,7 +2803,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
set.addAll(a.getLoanTransactionToRepaymentScheduleMappings()); set.addAll(a.getLoanTransactionToRepaymentScheduleMappings());
set.addAll(b.getLoanTransactionToRepaymentScheduleMappings()); set.addAll(b.getLoanTransactionToRepaymentScheduleMappings());
return new BalancesWithPaidInAdvance(a.getPrincipal().add(b.getPrincipal()), a.getInterest().add(b.getInterest()), 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 List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
final LocalDate transactionDate = loanTransaction.getTransactionDate();
// Find the last re-aged installment number final Optional<RepaymentPeriod> reAgeSpecialRepaymentPeriodOpt = ctx.getModel().repaymentPeriods().stream()
final OptionalInt lastReAgedInstallmentNumberOpt = installments.stream().filter(LoanRepaymentScheduleInstallment::isReAged) .filter(RepaymentPeriod::isReAgedEarlyRepaymentHolder).findFirst();
.mapToInt(LoanRepaymentScheduleInstallment::getInstallmentNumber).max(); final LoanRepaymentScheduleInstallment installmentWithMovedPaidAmounts = reAgeSpecialRepaymentPeriodOpt
.map(repaymentPeriod -> createInstallmentWithMovedPaidAmounts(ctx, loanTransaction, installments, repaymentPeriod))
.orElse(null);
if (lastReAgedInstallmentNumberOpt.isPresent()) { final List<LoanCharge> liftedLoanCharges = new ArrayList<>();
final int lastReAgedInstallmentNumber = lastReAgedInstallmentNumberOpt.getAsInt(); installments.stream().filter(installment -> !installment.getDueDate().isBefore(transactionDate)).forEach(i -> {
// Remove installments with numbers greater than the last re-aged installment liftedLoanCharges.addAll(getLoanChargesOfInstallment(ctx.getCharges(), i, 1));
final List<LoanRepaymentScheduleInstallment> installmentsToRemove = installments.stream().filter(i -> i != null });
&& !i.isAdditional() && i.getInstallmentNumber() != null && i.getInstallmentNumber() > lastReAgedInstallmentNumber)
.toList(); installments.removeIf(i -> i.getInstallmentNumber() != null && !i.isDownPayment() && !i.getDueDate().isBefore(transactionDate));
installmentsToRemove.forEach(installments::remove);
} 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 -> { ctx.getModel().repaymentPeriods().forEach(rp -> {
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream() LoanRepaymentScheduleInstallment installment = null;
.filter(ri -> ri.getFromDate().equals(rp.getFromDate()) && !ri.isDownPayment()).findFirst().orElse(null); while (iterator.hasNext() && (installment == null || installment.isAdditional() || installment.isDownPayment())) {
final LocalDate transactionDate = loanTransaction.getTransactionDate(); installment = iterator.next();
installmentCounter.getAndIncrement();
}
if (installment != null) { if (installment != null) {
installment.setFromDate(rp.getFromDate()); if (installment.isNotFullyPaidOff() || !installment.getFromDate().isBefore(reAgePeriodStartDate)) {
installment.setDueDate(rp.getDueDate()); installment.setFromDate(rp.getFromDate());
installment.setDueDate(rp.getDueDate());
if (rp.getEmi().isZero()) { if (rp.getEmi().isZero()) {
installment.updatePrincipal(BigDecimal.ZERO); installment.updatePrincipal(ZERO);
installment.updateInterestCharged(BigDecimal.ZERO); installment.updateInterestCharged(ZERO);
} else { } else {
installment.updatePrincipal(rp.getDuePrincipal().getAmount()); installment.updatePrincipal(rp.getDuePrincipal().getAmount());
installment.updateInterestCharged(rp.getDueInterest().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 { } else {
final LoanRepaymentScheduleInstallment lastInstallment = ctx.getInstallments().getLast(); if (rp.isReAgedEarlyRepaymentHolder()) {
final LoanRepaymentScheduleInstallment newInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment( iterator.add(installmentWithMovedPaidAmounts);
loanTransaction.getLoan(), lastInstallment.getInstallmentNumber() + 1, rp.getFromDate(), rp.getDueDate(), } else {
rp.getDuePrincipal().getAmount(), null, null, null); 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()) { if (rp.getDueInterest().isGreaterThanZero()) {
newInstallment.addToInterest(transactionDate, rp.getDueInterest()); 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) { 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, final BigDecimal totalPrincipal = periodsToRemove.stream().map(rp -> rp.getDuePrincipal().getAmount()).reduce(BigDecimal.ZERO,
BigDecimal::add); BigDecimal::add);
final BigDecimal newInterest = emiCalculator final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(transactionCtx.getModel(), lastPeriod.getFromDate(),
.getPeriodInterestTillDate(transactionCtx.getModel(), lastPeriod.getDueDate(), transactionDate, false).getAmount(); lastPeriod.getDueDate(), transactionDate, false).getAmount();
lastPeriod.setEmi(lastPeriod.getDuePrincipal().add(totalPrincipal).add(newInterest)); lastPeriod.setEmi(lastPeriod.getDuePrincipal().add(totalPrincipal).add(newInterest));
@@ -3074,9 +3142,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
} else if (isInPeriod) { } else if (isInPeriod) {
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
&& loan.isInterestBearingAndInterestRecalculationEnabled()) { && loan.isInterestBearingAndInterestRecalculationEnabled()) {
interest = emiCalculator interest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), installment.getFromDate(),
.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), installment.getDueDate(), chargeOffDate, true) installment.getDueDate(), chargeOffDate, true).getAmount();
.getAmount();
} else { } else {
final BigDecimal totalInterest = installment.getInterestOutstanding(currency).getAmount(); final BigDecimal totalInterest = installment.getInterestOutstanding(currency).getAmount();
if (LoanChargeOffBehaviour.ZERO_INTEREST.equals(loan.getLoanProductRelatedDetail().getChargeOffBehaviour()) if (LoanChargeOffBehaviour.ZERO_INTEREST.equals(loan.getLoanProductRelatedDetail().getChargeOffBehaviour())
@@ -3500,18 +3567,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
final List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments(); final List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
final LocalDate reAgingStartDate = loanTransaction.getLoanReAgeParameter().getStartDate(); 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(), final Money interestFromZeroedInstallments = emiCalculator.getOutstandingInterestTillDate(ctx.getModel(),
loanTransaction.getTransactionDate()); loanTransaction.getTransactionDate());
@@ -3537,13 +3592,13 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
loan.getLoanProduct().getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalculation()) loan.getLoanProduct().getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalculation())
.mc(mc).build(); .mc(mc).build();
LocalDate reAgePeriodStartDate = calculateFirstReAgedPeriodStartDate(loanTransaction); final LocalDate reAgePeriodStartDate = calculateFirstReAgedPeriodStartDate(loanTransaction);
LocalDate reageFirstDueDate = loanTransaction.getLoanReAgeParameter().getStartDate(); final LocalDate reAgeFirstDueDate = loanTransaction.getLoanReAgeParameter().getStartDate();
// Update the existing model with re-aged periods // Update the existing model with re-aged periods
emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx.getModel(), reAgePeriodStartDate, reageFirstDueDate, emiCalculator.updateModelRepaymentPeriodsDuringReAge(ctx.getModel(), reAgePeriodStartDate, reAgeFirstDueDate,
loanTransaction.getTransactionDate(), loanApplicationTerms, mc); loanTransaction.getTransactionDate(), loanApplicationTerms, mc);
updateInstallmentsByModelForReAging(loanTransaction, ctx); updateInstallmentsByModelForReAging(loanTransaction, ctx, reAgePeriodStartDate);
loanTransaction.updateComponentsAndTotal(totalOutstandingPrincipal, interestFromZeroedInstallments, Money.zero(currency), loanTransaction.updateComponentsAndTotal(totalOutstandingPrincipal, interestFromZeroedInstallments, Money.zero(currency),
Money.zero(currency)); Money.zero(currency));
@@ -126,19 +126,20 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
chargesDueAtTimeOfDisbursement, false, mc); chargesDueAtTimeOfDisbursement, false, mc);
repaymentPeriod.setPeriodNumber(scheduleParams.getInstalmentNumber()); repaymentPeriod.setPeriodNumber(scheduleParams.getInstalmentNumber());
emiCalculator.findRepaymentPeriod(interestScheduleModel, repaymentPeriod.getDueDate()).ifPresent(interestRepaymentPeriod -> { emiCalculator.findRepaymentPeriod(interestScheduleModel, repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate())
final Money principalDue = interestRepaymentPeriod.getDuePrincipal(); .ifPresent(interestRepaymentPeriod -> {
final Money interestDue = interestRepaymentPeriod.getDueInterest(); final Money principalDue = interestRepaymentPeriod.getDuePrincipal();
final Money interestDue = interestRepaymentPeriod.getDueInterest();
repaymentPeriod.addPrincipalAmount(principalDue); repaymentPeriod.addPrincipalAmount(principalDue);
repaymentPeriod.addInterestAmount(interestDue); repaymentPeriod.addInterestAmount(interestDue);
repaymentPeriod.setOutstandingLoanBalance(interestRepaymentPeriod.getOutstandingLoanBalance()); repaymentPeriod.setOutstandingLoanBalance(interestRepaymentPeriod.getOutstandingLoanBalance());
scheduleParams.addTotalCumulativePrincipal(principalDue); scheduleParams.addTotalCumulativePrincipal(principalDue);
scheduleParams.addTotalCumulativeInterest(interestDue); scheduleParams.addTotalCumulativeInterest(interestDue);
// add everything // add everything
scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue, mc)); scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue, mc));
}); });
applyChargesForCurrentPeriod(repaymentPeriod, loanCharges, scheduleParams, currency, mc); applyChargesForCurrentPeriod(repaymentPeriod, loanCharges, scheduleParams, currency, mc);
periods.add(repaymentPeriod); periods.add(repaymentPeriod);
@@ -254,7 +255,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
} }
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate); Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate);
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow(); 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 // Private, internal methods
@@ -61,7 +61,8 @@ public interface EMICalculator {
/** /**
* Find repayment period based on Due Date. * 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. * 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. * This method used for pay interest portion during the repayment transaction.
*/ */
void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate, void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
Money interestAmount); LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money interestAmount);
/** /**
* This method used for pay principal portion during the repayment transaction. * This method used for pay principal portion during the repayment transaction.
*/ */
void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate, void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
Money principalAmount); LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money principalAmount);
/** /**
* This method used for credit principal portion. This method increases the outstanding balance. This method creates * 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. * This method gives back the maximum of the due principal and maximum of the due interest for a requested day.
*/ */
@NotNull @NotNull
PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate, PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate targetDate); @NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate);
/** /**
* Gives back the sum of the interest from the whole model on the given date. * Gives back the sum of the interest from the whole model on the given date.
*/ */
@NotNull @NotNull
Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate, Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate targetDate, boolean includeChargebackInterest); @NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate, boolean includeChargebackInterest);
Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel interestScheduleModel, LocalDate targetDate); Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel interestScheduleModel, LocalDate targetDate);
@@ -112,11 +112,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override @Override
public Optional<RepaymentPeriod> findRepaymentPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel, public Optional<RepaymentPeriod> findRepaymentPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel,
final LocalDate repaymentPeriodDueDate) { final LocalDate repaymentPeriodFromDate, final LocalDate repaymentPeriodDueDate) {
if (scheduleModel == null) { if (scheduleModel == null) {
return Optional.empty(); return Optional.empty();
} }
return scheduleModel.findRepaymentPeriodByDueDate(repaymentPeriodDueDate); return scheduleModel.findRepaymentPeriodByFromAndDueDate(repaymentPeriodFromDate, repaymentPeriodDueDate);
} }
/** /**
@@ -263,9 +263,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
} }
@Override @Override
public void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate, public void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
Money interestAmount) { LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money interestAmount) {
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate); Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodFromDate, repaymentPeriodDueDate);
Optional<RepaymentPeriod> latestNotLastOpenRepaymentPeriodBeforeDate = getLatestNotLastOpenRepaymentPeriodBeforeDate(scheduleModel, Optional<RepaymentPeriod> latestNotLastOpenRepaymentPeriodBeforeDate = getLatestNotLastOpenRepaymentPeriodBeforeDate(scheduleModel,
transactionDate); transactionDate);
@@ -280,12 +280,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
} }
@Override @Override
public void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, public void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate,
LocalDate transactionDate, Money principalAmount) { LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money principalAmount) {
if (MathUtil.isEmpty(principalAmount)) { if (MathUtil.isEmpty(principalAmount)) {
return; return;
} }
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate); Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodFromDate, repaymentPeriodDueDate);
repaymentPeriod.ifPresent(rp -> rp.addPaidPrincipalAmount(principalAmount)); repaymentPeriod.ifPresent(rp -> rp.addPaidPrincipalAmount(principalAmount));
// If it is paid late, we need to calculate with the period due date // If it is paid late, we need to calculate with the period due date
LocalDate balanceCorrectionDate = DateUtils.isBefore(repaymentPeriodDueDate, transactionDate) ? repaymentPeriodDueDate LocalDate balanceCorrectionDate = DateUtils.isBefore(repaymentPeriodDueDate, transactionDate) ? repaymentPeriodDueDate
@@ -379,11 +379,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*/ */
@Override @Override
@NotNull @NotNull
public PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate, public PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate targetDate) { @NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate) {
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel, ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
targetDate); targetDate);
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow(); RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate
.findRepaymentPeriodByFromAndDueDate(periodFromDate, periodDueDate).orElseThrow();
long notFullyRepaidRepaymentPeriodCount = recalculatedScheduleModelTillDate.repaymentPeriods().stream() long notFullyRepaidRepaymentPeriodCount = recalculatedScheduleModelTillDate.repaymentPeriods().stream()
.filter(rp -> !rp.isFullyPaid()).count(); .filter(rp -> !rp.isFullyPaid()).count();
boolean multiplePeriodIsUnpaid = notFullyRepaidRepaymentPeriodCount > 1L; boolean multiplePeriodIsUnpaid = notFullyRepaidRepaymentPeriodCount > 1L;
@@ -413,11 +414,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override @Override
@NotNull @NotNull
public Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate, public Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
@NotNull LocalDate targetDate, boolean includeCreditedInterest) { @NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate, boolean includeCreditedInterest) {
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel, ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
targetDate); targetDate);
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow(); RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate
.findRepaymentPeriodByFromAndDueDate(periodFromDate, periodDueDate).orElseThrow();
return includeCreditedInterest ? repaymentPeriod.getCalculatedDueInterest() return includeCreditedInterest ? repaymentPeriod.getCalculatedDueInterest()
: repaymentPeriod.getCalculatedDueInterest().minus(repaymentPeriod.getCreditedInterest(), : repaymentPeriod.getCalculatedDueInterest().minus(repaymentPeriod.getCreditedInterest(),
recalculatedScheduleModelTillDate.mc()); recalculatedScheduleModelTillDate.mc());
@@ -572,15 +574,20 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override @Override
public void updateModelRepaymentPeriodsDuringReAge(final ProgressiveLoanInterestScheduleModel scheduleModel, 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) { 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, final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generateTemporaryReAgedScheduleModel(loanApplicationTerms,
mc, reAgePeriodStartDate); mc, reAgePeriodStartDate);
mergeNewInterestScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, reageFirstDueDate, targetDate); mergeNewInterestScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, reAgeFirstDueDate, targetDate,
paidBalancesFromTransactionDate);
} }
@Override @Override
@@ -634,7 +641,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
private List<RepaymentPeriod> findPossiblyOverdueRepaymentPeriods(final LocalDate targetDate, private List<RepaymentPeriod> findPossiblyOverdueRepaymentPeriods(final LocalDate targetDate,
final ProgressiveLoanInterestScheduleModel model) { final ProgressiveLoanInterestScheduleModel model) {
return model.repaymentPeriods().stream() // 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, private boolean adjustOverduePrincipal(final LocalDate currentDate, final RepaymentPeriod currentInstallment,
@@ -694,8 +703,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* significantly from other periods. * significantly from other periods.
*/ */
private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveLoanInterestScheduleModel scheduleModel, private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveLoanInterestScheduleModel scheduleModel,
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LocalDate reageFirstDueDate, final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LocalDate reAgeFirstDueDate,
LocalDate targetDate) { final LocalDate targetDate, final OutstandingDetails paidBalancesFromTransactionDate) {
final List<RepaymentPeriod> newPeriods = temporaryReAgedScheduleModel.repaymentPeriods(); final List<RepaymentPeriod> newPeriods = temporaryReAgedScheduleModel.repaymentPeriods();
if (newPeriods.isEmpty()) { if (newPeriods.isEmpty()) {
@@ -706,46 +715,44 @@ public final class ProgressiveEMICalculator implements EMICalculator {
final RepaymentPeriod firstRepaymentPeriod = existingRepaymentPeriods.getFirst(); final RepaymentPeriod firstRepaymentPeriod = existingRepaymentPeriods.getFirst();
final Money disbursedAmount = firstRepaymentPeriod.getTotalDisbursedAmount(); final Money disbursedAmount = firstRepaymentPeriod.getTotalDisbursedAmount();
final Optional<RepaymentPeriod> firstExistingReAgedRepaymentPeriodOpt = existingRepaymentPeriods.stream() if (!paidBalancesFromTransactionDate.getOutstandingInterest().isZero()
.filter(period -> period.getDueDate().equals(reageFirstDueDate)).findFirst(); || !paidBalancesFromTransactionDate.getOutstandingPrincipal().isZero()) {
createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(scheduleModel,
paidBalancesFromTransactionDate.getOutstandingPrincipal(), paidBalancesFromTransactionDate.getOutstandingInterest(),
false);
}
for (final RepaymentPeriod newPeriod : newPeriods) { 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 newPrincipal = newPeriod.getDuePrincipal();
final Money newInterest = newPeriod.getDueInterest(); final Money newInterest = newPeriod.getDueInterest();
final RepaymentPeriod rp = RepaymentPeriod.create( if (reAgeFirstDueDate.isEqual(scheduleModel.getStartDate()) && newPeriod.equals(newPeriods.getFirst())) {
previousExistingRepaymentPeriodOpt.orElseGet(existingRepaymentPeriods::getLast), newPeriod.getFromDate(), final RepaymentPeriod firstPeriod = existingRepaymentPeriods.getFirst();
newPeriod.getDueDate(), newPrincipal.add(newInterest), scheduleModel.mc(), scheduleModel.loanProductRelatedDetail()); firstPeriod.setDueDate(newPeriod.getDueDate());
rp.setTotalDisbursedAmount(disbursedAmount); 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() final List<RepaymentPeriod> reAgedRepaymentPeriods = existingRepaymentPeriods.stream()
.filter(repaymentPeriod -> (!repaymentPeriod.getFromDate().isBefore(reageFirstDueDate) .filter(repaymentPeriod -> (repaymentPeriod.getFromDate().isEqual(targetDate)
|| repaymentPeriod.getDueDate().isEqual(reageFirstDueDate)) && repaymentPeriod.getDueDate().isEqual(reAgeFirstDueDate))
&& !repaymentPeriod.getDueDate().isAfter(newPeriods.getLast().getDueDate())) || !repaymentPeriod.getFromDate().isBefore(reAgeFirstDueDate))
.toList(); .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); calculateOutstandingBalance(scheduleModel);
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, targetDate); calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, targetDate);
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, reAgedRepaymentPeriods); checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, reAgedRepaymentPeriods);
@@ -773,13 +780,20 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* principal that was moved. * principal that was moved.
*/ */
private static void moveOutstandingAmountsFromPeriodsBeforeReAging(final List<RepaymentPeriod> existingRepaymentPeriods, private static void moveOutstandingAmountsFromPeriodsBeforeReAging(final List<RepaymentPeriod> existingRepaymentPeriods,
final LocalDate reAgingStartDate) { final LocalDate transactionDate) {
final List<RepaymentPeriod> periodsBeforeReAging = existingRepaymentPeriods.stream() 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 -> { 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(); 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.setEmi(rp.getTotalPaidAmount());
rp.setOutstandingMovedDueToReAging(true); rp.setOutstandingMovedDueToReAging(true);
}); });
@@ -876,10 +890,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
if (newScheduleModel == null) { if (newScheduleModel == null) {
newScheduleModel = scheduleModel.deepCopy(mc); newScheduleModel = scheduleModel.deepCopy(mc);
} }
final LocalDate relatedPeriodsFirstFromDate = relatedRepaymentPeriods.getFirst().getFromDate();
final LocalDate relatedPeriodsFirstDueDate = relatedRepaymentPeriods.getFirst().getDueDate(); final LocalDate relatedPeriodsFirstDueDate = relatedRepaymentPeriods.getFirst().getDueDate();
newScheduleModel.repaymentPeriods().forEach(period -> { newScheduleModel.repaymentPeriods().forEach(period -> {
if (!period.getDueDate().isBefore(relatedPeriodsFirstDueDate) if (!period.getFromDate().isBefore(relatedPeriodsFirstFromDate) && !period.getDueDate().isBefore(relatedPeriodsFirstDueDate)
&& !adjustedEqualMonthlyInstallmentValue.isLessThan(period.getTotalPaidAmount())) { && !adjustedEqualMonthlyInstallmentValue.isLessThan(period.getTotalPaidAmount())
&& !period.isReAgedEarlyRepaymentHolder()) {
period.setEmi(adjustedEqualMonthlyInstallmentValue); period.setEmi(adjustedEqualMonthlyInstallmentValue);
period.setOriginalEmi(adjustedEqualMonthlyInstallmentValue); period.setOriginalEmi(adjustedEqualMonthlyInstallmentValue);
} }
@@ -891,7 +907,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
} }
final Iterator<RepaymentPeriod> relatedPeriodFromNewModelIterator = newScheduleModel.repaymentPeriods().stream()// 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();// .toList().iterator();//
relatedRepaymentPeriods.forEach(relatedRepaymentPeriod -> { relatedRepaymentPeriods.forEach(relatedRepaymentPeriod -> {
@@ -1071,7 +1088,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
calculatedDate = seedDate.plus(multiplicator, chronoUnit); calculatedDate = seedDate.plus(multiplicator, chronoUnit);
multiplicator++; multiplicator++;
} while (calculatedDate.isBefore(repaymentPeriod.getDueDate())); } 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 @Override
public Money getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate subjectDate) { public Money getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate subjectDate) {
return scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueDate) // return scheduleModel.repaymentPeriods().stream() //
.map(repaymentPeriodDueDate -> getDueAmounts(scheduleModel, repaymentPeriodDueDate, subjectDate) // .map(repaymentPeriod -> getDueAmounts(scheduleModel, repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate(),
subjectDate) //
.getDueInterest()) // .getDueInterest()) //
.reduce(scheduleModel.zero(), Money::add); // .reduce(scheduleModel.zero(), Money::add); //
} }
@@ -1663,7 +1684,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
if (!paidBalancesFromTransactionDate.getOutstandingInterest().isZero() if (!paidBalancesFromTransactionDate.getOutstandingInterest().isZero()
|| !paidBalancesFromTransactionDate.getOutstandingPrincipal().isZero()) { || !paidBalancesFromTransactionDate.getOutstandingPrincipal().isZero()) {
createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(interestSchedule, createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(interestSchedule,
paidBalancesFromTransactionDate.getOutstandingPrincipal(), paidBalancesFromTransactionDate.getOutstandingInterest()); paidBalancesFromTransactionDate.getOutstandingPrincipal(), paidBalancesFromTransactionDate.getOutstandingInterest(),
true);
addFirstReAgedPeriod(interestSchedule, interestSchedule.getLastRepaymentPeriod());
} }
updateModelForReageEqualAmortization(interestSchedule, reageParameter, reAgedRepaymentPeriods, isAfterOriginalMaturityDate); updateModelForReageEqualAmortization(interestSchedule, reageParameter, reAgedRepaymentPeriods, isAfterOriginalMaturityDate);
@@ -1717,18 +1740,23 @@ public final class ProgressiveEMICalculator implements EMICalculator {
} }
} }
private void createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(ProgressiveLoanInterestScheduleModel interestSchedule, private void createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(final ProgressiveLoanInterestScheduleModel interestSchedule,
Money totalPaidPrincipal, Money totalPaidInterest) { final Money totalPaidPrincipal, final Money totalPaidInterest, final boolean isReAged) {
RepaymentPeriod targetPeriod = interestSchedule.getLastRepaymentPeriod(); final RepaymentPeriod targetPeriod = interestSchedule.getLastRepaymentPeriod();
Money paidInterestToAdd = totalPaidInterest.minus(targetPeriod.getPaidInterest()); final Money paidInterestToAdd = totalPaidInterest.minus(targetPeriod.getPaidInterest());
Money paidPrincipalToAdd = totalPaidPrincipal.minus(targetPeriod.getPaidPrincipal()); final Money paidPrincipalToAdd = totalPaidPrincipal.minus(targetPeriod.getPaidPrincipal());
targetPeriod.addPaidInterestAmount(paidInterestToAdd); targetPeriod.addPaidInterestAmount(paidInterestToAdd);
targetPeriod.addPaidPrincipalAmount(paidPrincipalToAdd); targetPeriod.addPaidPrincipalAmount(paidPrincipalToAdd);
targetPeriod.setEmi(targetPeriod.getTotalPaidAmount()); targetPeriod.setEmi(targetPeriod.getTotalPaidAmount());
targetPeriod.setReAged(true); if (isReAged) {
targetPeriod.setReAged(true);
}
targetPeriod.setReAgedEarlyRepaymentHolder(true); targetPeriod.setReAgedEarlyRepaymentHolder(true);
calculateRateFactorForRepaymentPeriod(targetPeriod, interestSchedule);
}
private static void addFirstReAgedPeriod(ProgressiveLoanInterestScheduleModel interestSchedule, RepaymentPeriod targetPeriod) {
RepaymentPeriod repaymentPeriodToInsert = RepaymentPeriod.create(targetPeriod, targetPeriod.getDueDate(), RepaymentPeriod repaymentPeriodToInsert = RepaymentPeriod.create(targetPeriod, targetPeriod.getDueDate(),
interestSchedule.getMaturityDate(), interestSchedule.zero(), interestSchedule.mc(), interestSchedule.getMaturityDate(), interestSchedule.zero(), interestSchedule.mc(),
interestSchedule.loanProductRelatedDetail()); interestSchedule.loanProductRelatedDetail());
@@ -143,12 +143,14 @@ public class ProgressiveLoanInterestScheduleModel {
interestRates.add(new InterestRate(newInterestEffectiveDate, newInterestRate)); 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) { if (repaymentPeriodDueDate == null) {
return Optional.empty(); return Optional.empty();
} }
return repaymentPeriods.stream()// return repaymentPeriods.stream()//
.filter(repaymentPeriodItem -> DateUtils.isEqual(repaymentPeriodItem.getDueDate(), repaymentPeriodDueDate))// .filter(repaymentPeriodItem -> DateUtils.isEqual(repaymentPeriodItem.getFromDate(), repaymentPeriodFromDate)
&& DateUtils.isEqual(repaymentPeriodItem.getDueDate(), repaymentPeriodDueDate))//
.findFirst(); .findFirst();
} }
@@ -507,9 +507,10 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
ArgumentCaptor<Money> paidPortionCaptor = ArgumentCaptor.forClass(Money.class); ArgumentCaptor<Money> paidPortionCaptor = ArgumentCaptor.forClass(Money.class);
ArgumentCaptor<LocalDate> payDateCaptor = ArgumentCaptor.forClass(LocalDate.class); ArgumentCaptor<LocalDate> payDateCaptor = ArgumentCaptor.forClass(LocalDate.class);
final LocalDate repaymentPeriodFromDate = transactionDate.minusMonths(1);
// Verify `payPrincipal` is called when allocation type is DUE_PRINCIPAL // Verify `payPrincipal` is called when allocation type is DUE_PRINCIPAL
Mockito.verify(emiCalculator, atLeastOnce()).payPrincipal(eq(model), eq(transactionDate), payDateCaptor.capture(), Mockito.verify(emiCalculator, atLeastOnce()).payPrincipal(eq(model), eq(repaymentPeriodFromDate), eq(transactionDate),
paidPortionCaptor.capture()); payDateCaptor.capture(), paidPortionCaptor.capture());
Money paidPortion = paidPortionCaptor.getValue(); Money paidPortion = paidPortionCaptor.getValue();
LocalDate payDate = payDateCaptor.getValue(); LocalDate payDate = payDateCaptor.getValue();
@@ -447,10 +447,13 @@ class ProgressiveEMICalculatorTest {
final Money disbursedAmount = toMoney(100.0); final Money disbursedAmount = toMoney(100.0);
emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount); 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.payInterest(interestModel, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payPrincipal(interestModel, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43)); 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 BigDecimal interestRateNewValue = BigDecimal.valueOf(4.0);
final LocalDate interestChangeDate = LocalDate.of(2024, 2, 2); final LocalDate interestChangeDate = LocalDate.of(2024, 2, 2);
@@ -495,7 +498,8 @@ class ProgressiveEMICalculatorTest {
final Money disbursedAmount = toMoney(100.0); final Money disbursedAmount = toMoney(100.0);
emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount); 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 BigDecimal interestRateNewValue = BigDecimal.valueOf(4.0);
final LocalDate interestChangeDate = LocalDate.of(2024, 1, 20); final LocalDate interestChangeDate = LocalDate.of(2024, 1, 20);
@@ -588,12 +592,14 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount); emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// schedule 1st period 1st day // 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(17.01, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.0, toDouble(dueAmounts.getDueInterest())); Assertions.assertEquals(0.0, toDouble(dueAmounts.getDueInterest()));
// schedule 2nd period last day // 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(16.52, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.49, toDouble(dueAmounts.getDueInterest())); Assertions.assertEquals(0.49, toDouble(dueAmounts.getDueInterest()));
@@ -603,13 +609,15 @@ class ProgressiveEMICalculatorTest {
final Money op1stCorrectionAmount = toMoney(16.77); final Money op1stCorrectionAmount = toMoney(16.77);
// get remaining balance and dues for a date // get remaining balance and dues for a date
final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, op1stCorrectionPeriodDueDate, final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1),
op1stCorrectionDate); op1stCorrectionPeriodDueDate, op1stCorrectionDate);
Assertions.assertEquals(16.77, toDouble(repaymentDetails1st.getDuePrincipal())); Assertions.assertEquals(16.77, toDouble(repaymentDetails1st.getDuePrincipal()));
Assertions.assertEquals(0.24, toDouble(repaymentDetails1st.getDueInterest())); Assertions.assertEquals(0.24, toDouble(repaymentDetails1st.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, op1stCorrectionPeriodDueDate, op1stCorrectionDate, op1stCorrectionAmount); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), op1stCorrectionPeriodDueDate, op1stCorrectionDate,
emiCalculator.payInterest(interestSchedule, op1stCorrectionPeriodDueDate, op1stCorrectionDate, toMoney(0.24)); 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, 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); 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); final Money op2ndCorrectionAmount = toMoney(16.42);
// get remaining balance and dues for a date // get remaining balance and dues for a date
final PeriodDueDetails repaymentDetails2st = emiCalculator.getDueAmounts(interestSchedule, op2ndCorrectionPeriodDueDate, final PeriodDueDetails repaymentDetails2st = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1),
op2ndCorrectionDate); op2ndCorrectionPeriodDueDate, op2ndCorrectionDate);
Assertions.assertEquals(16.81, toDouble(repaymentDetails2st.getDuePrincipal())); Assertions.assertEquals(16.81, toDouble(repaymentDetails2st.getDuePrincipal()));
Assertions.assertEquals(0.20, toDouble(repaymentDetails2st.getDueInterest())); Assertions.assertEquals(0.20, toDouble(repaymentDetails2st.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, op2ndCorrectionPeriodDueDate, op2ndCorrectionDate, op2ndCorrectionAmount); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), op2ndCorrectionPeriodDueDate, op2ndCorrectionDate,
emiCalculator.payInterest(interestSchedule, op2ndCorrectionPeriodDueDate, op2ndCorrectionDate, toMoney(0.49)); 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, 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); 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 // check numbers on last period due date
LocalDate periodDueDate = LocalDate.of(2024, 7, 1); LocalDate periodDueDate = LocalDate.of(2024, 7, 1);
LocalDate payDate = 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(16.75, toDouble(repaymentDetails3rd.getDuePrincipal()));
Assertions.assertEquals(0.1, toDouble(repaymentDetails3rd.getDueInterest())); Assertions.assertEquals(0.1, toDouble(repaymentDetails3rd.getDueInterest()));
// check numbers after the last period due date // check numbers after the last period due date
periodDueDate = LocalDate.of(2024, 7, 1); periodDueDate = LocalDate.of(2024, 7, 1);
payDate = LocalDate.of(2024, 7, 15); 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(16.75, toDouble(repaymentDetails4th.getDuePrincipal()));
Assertions.assertEquals(0.1, toDouble(repaymentDetails4th.getDueInterest())); Assertions.assertEquals(0.1, toDouble(repaymentDetails4th.getDueInterest()));
@@ -700,30 +712,41 @@ class ProgressiveEMICalculatorTest {
final Money op1stCorrectionAmount = toMoney(15.0); final Money op1stCorrectionAmount = toMoney(15.0);
// get remaining balance and dues for a date // get remaining balance and dues for a date
final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, op1stCorrectionPeriodDueDate, final PeriodDueDetails repaymentDetails1st = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 2, 1),
op1stCorrectionDate); op1stCorrectionPeriodDueDate, op1stCorrectionDate);
Assertions.assertEquals(16.77, toDouble(repaymentDetails1st.getDuePrincipal())); Assertions.assertEquals(16.77, toDouble(repaymentDetails1st.getDuePrincipal()));
Assertions.assertEquals(0.24, toDouble(repaymentDetails1st.getDueInterest())); Assertions.assertEquals(0.24, toDouble(repaymentDetails1st.getDueInterest()));
// check getDueAmounts forcast // 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(16.52, toDouble(details.getDuePrincipal()));
Assertions.assertEquals(0.49, toDouble(details.getDueInterest())); Assertions.assertEquals(0.49, toDouble(details.getDueInterest()));
// apply balance change and check again // apply balance change and check again
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), op1stCorrectionDate, op1stCorrectionAmount); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), op1stCorrectionDate,
details = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1)); 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(16.52, toDouble(details.getDuePrincipal()));
Assertions.assertEquals(0.49, toDouble(details.getDueInterest())); Assertions.assertEquals(0.49, toDouble(details.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15), toMoney(1.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15), toMoney(0.58)); toMoney(1.43));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15), toMoney(16.77)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 15),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15), toMoney(0.24)); toMoney(0.58));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 2, 15), toMoney(17.01)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 2, 15), toMoney(17.01)); toMoney(16.77));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 2, 15), toMoney(17.01)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 2, 15),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 2, 15), toMoney(15.77)); 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 // check periods in model
@@ -766,28 +789,37 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount); emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// get remaining balance and dues on due date // 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(16.43, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.58, toDouble(dueAmounts.getDueInterest())); Assertions.assertEquals(0.58, toDouble(dueAmounts.getDueInterest()));
// check numbers on payoff date // 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(16.75, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.26, toDouble(dueAmounts.getDueInterest())); Assertions.assertEquals(0.26, toDouble(dueAmounts.getDueInterest()));
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15), toMoney(16.75)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 15), toMoney(0.26)); 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 // check again numbers are zero
// dueAmounts = emiCalculator.getdueAmounts(interestSchedule, LocalDate.of(2024, 2, 1), // dueAmounts = emiCalculator.getdueAmounts(interestSchedule, LocalDate.of(2024, 2, 1),
// 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, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 15),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 15), toMoney(17.01)); 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, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 15),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 1, 15), toMoney(17.01)); 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(15.21, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.5, toDouble(dueAmounts.getDueInterest())); 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, 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); 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 // check periods in model
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.26, 16.75, 0.0); 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())); Assertions.assertEquals(6.15, toDouble(interestSchedule.getTotalDueInterest()));
// pay back the whole loan on first day // 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, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 1), toMoney(51.03)); 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, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 1, 1), toMoney(51.03)); 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, 3, 1), LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 1),
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 1), toMoney(44.85)); 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); checkTotalInterestDue(interestSchedule, 0.0);
@@ -1208,11 +1247,12 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount); emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
for (int i = 0; i < 12; i++) { for (int i = 0; i < 12; i++) {
LocalDate fromDate = expectedRepaymentPeriods.get(i).getFromDate();
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate(); 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(); Money duePrincipal = dueAmounts.getDuePrincipal();
if (duePrincipal.isGreaterThanZero()) { 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 dueDate = LocalDate.of(2024, 2, 1);
final LocalDate startDay = LocalDate.of(2024, 1, 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.creditInterest(interestModel, startDay.plusDays(3), toMoney(0.0));
emiCalculator.addBalanceCorrection(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, 4, 857.71, 14.33, 843.38, 850.76, false);
checkPeriod(interestSchedule, 5, 857.73, 6.97, 850.76, 0, 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))); 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))); Money.of(currency, BigDecimal.valueOf(36.95)));
checkPeriod(interestSchedule, 0, 857.71, 36.95, 820.76, 4179.24, true); checkPeriod(interestSchedule, 0, 857.71, 36.95, 820.76, 4179.24, true);
@@ -2062,8 +2102,9 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount); emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
LocalDate fromDate = expectedRepaymentPeriods.get(i).getFromDate();
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate(); 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(); Money duePrincipal = dueAmounts.getDuePrincipal();
emiCalculator.addBalanceCorrection(interestSchedule, dueDate, duePrincipal); emiCalculator.addBalanceCorrection(interestSchedule, dueDate, duePrincipal);
} }
@@ -2105,14 +2146,15 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount); 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))); 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))); Money.of(currency, BigDecimal.valueOf(820.76)));
for (int i = 1; i < 6; i++) { for (int i = 1; i < 6; i++) {
LocalDate fromDate = expectedRepaymentPeriods.get(i).getFromDate();
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate(); 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(); Money duePrincipal = dueAmounts.getDuePrincipal();
emiCalculator.addBalanceCorrection(interestSchedule, dueDate, duePrincipal); 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); checkPeriod(interestSchedule, 5, 0, 17.00, 0.005833333333, 0.0985833333276, 0.1, 16.9, 0.0);
// repay 1st period // repay 1st period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(0.58)); 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 // repay 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49)); 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 // full chargeback on duedate
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(17.01)); 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); checkPeriod(interestSchedule, 5, 0, 17.00, 0.005833333333, 0.0985833333276, 0.1, 16.9, 0.0);
// repay 1st period // repay 1st period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(0.58)); 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 // repay 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49)); 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 // partial chargeback
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(15.0)); 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); checkPeriod(interestSchedule, 5, 0, 17.00, 0.005833333333, 0.0985833333276, 0.1, 16.9, 0.0);
// repay 1st period // repay 1st period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1), toMoney(0.58)); 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 // repay 2nd period
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1),
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49)); 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 // full chargeback on duedate
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 15), toMoney(17.01)); emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 15), toMoney(17.01));
@@ -2623,13 +2677,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period // repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1); LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period // repay 2nd period
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback // chargeback
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
@@ -2681,13 +2735,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period // repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1); LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period // repay 2nd period
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback // chargeback
txnDate = LocalDate.of(2024, 7, 1); txnDate = LocalDate.of(2024, 7, 1);
@@ -2733,13 +2787,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period // repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1); LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period // repay 2nd period
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback // chargeback
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
@@ -2791,13 +2845,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period // repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1); LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period // repay 2nd period
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback // chargeback
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
@@ -2848,13 +2902,13 @@ class ProgressiveEMICalculatorTest {
// repay 1st period // repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1); LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period // repay 2nd period
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback 1st // chargeback 1st
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
@@ -3142,20 +3196,21 @@ class ProgressiveEMICalculatorTest {
// repay 1st period // repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1); LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
// repay 2nd period // repay 2nd period
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestSchedule, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestSchedule, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
// chargeback // chargeback
txnDate = LocalDate.of(2024, 3, 15); txnDate = LocalDate.of(2024, 3, 15);
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(16.52)); emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(16.52));
emiCalculator.creditInterest(interestSchedule, txnDate, toMoney(0.49)); 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.09, toDouble(interestSchedule.repaymentPeriods().get(2).getDuePrincipal()));
Assertions.assertEquals(33.35, toDouble(dueAmounts.getDuePrincipal())); Assertions.assertEquals(33.35, toDouble(dueAmounts.getDuePrincipal()));
Assertions.assertEquals(0.67, toDouble(dueAmounts.getDueInterest())); Assertions.assertEquals(0.67, toDouble(dueAmounts.getDueInterest()));
@@ -3204,10 +3259,10 @@ class ProgressiveEMICalculatorTest {
// repay 1st period // repay 1st period
LocalDate txnDate = LocalDate.of(2024, 2, 1); LocalDate txnDate = LocalDate.of(2024, 2, 1);
emiCalculator.payPrincipal(interestScheduleExpected, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestScheduleExpected, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestScheduleExpected, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestScheduleExpected, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
emiCalculator.payPrincipal(interestScheduleActual, txnDate, txnDate, toMoney(16.43)); emiCalculator.payPrincipal(interestScheduleActual, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(16.43));
emiCalculator.payInterest(interestScheduleActual, txnDate, txnDate, toMoney(0.58)); emiCalculator.payInterest(interestScheduleActual, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.58));
verifyAllPeriods(interestScheduleExpected, interestScheduleActual); verifyAllPeriods(interestScheduleExpected, interestScheduleActual);
interestScheduleActual = copyJson(interestScheduleExpected); interestScheduleActual = copyJson(interestScheduleExpected);
@@ -3215,10 +3270,10 @@ class ProgressiveEMICalculatorTest {
// repay 2nd period // repay 2nd period
txnDate = LocalDate.of(2024, 3, 1); txnDate = LocalDate.of(2024, 3, 1);
emiCalculator.payPrincipal(interestScheduleExpected, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestScheduleExpected, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestScheduleExpected, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestScheduleExpected, LocalDate.of(2024, 1, 1), txnDate, txnDate, toMoney(0.49));
emiCalculator.payPrincipal(interestScheduleActual, txnDate, txnDate, toMoney(16.52)); emiCalculator.payPrincipal(interestScheduleActual, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(16.52));
emiCalculator.payInterest(interestScheduleActual, txnDate, txnDate, toMoney(0.49)); emiCalculator.payInterest(interestScheduleActual, LocalDate.of(2024, 2, 1), txnDate, txnDate, toMoney(0.49));
verifyAllPeriods(interestScheduleExpected, interestScheduleActual); verifyAllPeriods(interestScheduleExpected, interestScheduleActual);
interestScheduleActual = copyJson(interestScheduleExpected); interestScheduleActual = copyJson(interestScheduleExpected);
@@ -3235,8 +3290,10 @@ class ProgressiveEMICalculatorTest {
interestScheduleActual = copyJson(interestScheduleExpected); interestScheduleActual = copyJson(interestScheduleExpected);
verifyAllPeriods(interestScheduleExpected, interestScheduleActual); verifyAllPeriods(interestScheduleExpected, interestScheduleActual);
PeriodDueDetails dueAmountsExpected = emiCalculator.getDueAmounts(interestScheduleExpected, LocalDate.of(2024, 4, 1), txnDate); PeriodDueDetails dueAmountsExpected = emiCalculator.getDueAmounts(interestScheduleExpected, LocalDate.of(2024, 3, 1),
PeriodDueDetails dueAmountsActual = emiCalculator.getDueAmounts(interestScheduleActual, LocalDate.of(2024, 4, 1), txnDate); 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.getDuePrincipal()), toDouble(dueAmountsActual.getDuePrincipal()));
Assertions.assertEquals(toDouble(dueAmountsExpected.getDueInterest()), toDouble(dueAmountsActual.getDueInterest())); 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); // 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 // 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))); Money.of(currency, BigDecimal.valueOf(256.29D)));
// simulate early repayment on day 0 // 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))); Money.of(currency, BigDecimal.valueOf(256.28D)));
// verify 1st and 4th installments are fully repaid // 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); // 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 // 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))); 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))); Money.of(currency, BigDecimal.valueOf(10.0D)));
checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 500.00, false); // 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); // checkPeriod(interestSchedule, 3, 260.00, 10.00, 250.00, 0.00, true); //
// simulate early repayment on day 0 // 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))); 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))); Money.of(currency, BigDecimal.valueOf(10.0D)));
// verify 1st and 4th installments are fully repaid // 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); checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date // 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))); 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))); Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period // 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))); Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR // 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))); 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))); Money.of(currency, BigDecimal.valueOf(2.57)));
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 1), 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 emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15),
.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal().negated()); 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); LocalDate reAgingStartDate = LocalDate.of(2024, 4, 20);
@@ -4360,9 +4421,9 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount); emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// repay EMI on first period due date // 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))); 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))); Money.of(currency, BigDecimal.valueOf(0.58)));
// chargeback // chargeback
@@ -4419,9 +4480,9 @@ class ProgressiveEMICalculatorTest {
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount); emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
// repay EMI on first period due date // 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))); 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))); Money.of(currency, BigDecimal.valueOf(0.58)));
// chargeback // chargeback
@@ -4487,26 +4548,30 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0); checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date // 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))); 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))); Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period // 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))); Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR // 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))); 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))); Money.of(currency, BigDecimal.valueOf(2.57)));
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 1), 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 emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15),
.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal().negated()); 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 transactionDate = LocalDate.of(2024, 4, 15);
LocalDate reAgingStartDate = LocalDate.of(2024, 4, 20); 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); checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date // 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))); 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))); Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period // 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))); Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR // 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))); 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))); Money.of(currency, BigDecimal.valueOf(2.57)));
emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 1), 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 emiCalculator.addBalanceCorrection(interestSchedule, LocalDate.of(2024, 4, 15),
.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 4, 1)).getDuePrincipal().negated()); 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 transactionDate = LocalDate.of(2024, 4, 15);
LocalDate reAgingStartDate = LocalDate.of(2024, 5, 1); 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); checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date // 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))); 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))); Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period // 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))); Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR // 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))); 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))); Money.of(currency, BigDecimal.valueOf(2.57)));
LocalDate transactionDate = LocalDate.of(2024, 7, 1); 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); checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 0.222283972598, 0.22, 17.25, 0.0);
// repay EMI on first period due date // 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))); 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))); Money.of(currency, BigDecimal.valueOf(1.33)));
// repay EMI on first period due date - next installment for 2nd period // 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))); Money.of(currency, BigDecimal.valueOf(17.43)));
// repay 20 for last installment on first period due date -- ie MIR // 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))); 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))); Money.of(currency, BigDecimal.valueOf(2.57)));
LocalDate transactionDate = LocalDate.of(2024, 7, 10); LocalDate transactionDate = LocalDate.of(2024, 7, 10);
@@ -4809,9 +4878,9 @@ class ProgressiveEMICalculatorTest {
private static void checkDailyInterest(final ProgressiveLoanInterestScheduleModel interestModel, final LocalDate repaymentPeriodDueDate, private static void checkDailyInterest(final ProgressiveLoanInterestScheduleModel interestModel, final LocalDate repaymentPeriodDueDate,
final LocalDate interestStartDay, final int dayOffset, final double dailyInterest, final double interest) { 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); interestStartDay.plusDays(dayOffset - 1), true);
Money currentInterest = emiCalculator.getPeriodInterestTillDate(interestModel, repaymentPeriodDueDate, Money currentInterest = emiCalculator.getPeriodInterestTillDate(interestModel, interestStartDay, repaymentPeriodDueDate,
interestStartDay.plusDays(dayOffset), true); interestStartDay.plusDays(dayOffset), true);
Assertions.assertEquals(dailyInterest, toDouble(currentInterest.minus(previousInterest))); Assertions.assertEquals(dailyInterest, toDouble(currentInterest.minus(previousInterest)));
Assertions.assertEquals(interest, toDouble(currentInterest)); Assertions.assertEquals(interest, toDouble(currentInterest));