FINERACT-2281: fix unrecognized interest handling when first period is unpaid
This commit is contained in:
+229
-11
@@ -6195,7 +6195,7 @@ Feature: LoanAccrualActivity
|
|||||||
Then Loan Repayment schedule has 12 periods, with the following data for periods:
|
Then Loan Repayment schedule has 12 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 |
|
||||||
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 1 | 31 | 21 April 2025 | | 218.82 | 123.64 | 0.77 | 0.0 | 0.0 | 124.41 | 83.64 | 83.64 | 0.0 | 40.77 |
|
| 1 | 31 | 21 April 2025 | | 218.82 | 123.64 | 0.84 | 0.0 | 0.0 | 124.48 | 83.64 | 83.64 | 0.0 | 40.84 |
|
||||||
| 2 | 30 | 21 May 2025 | 21 March 2025 | 195.18 | 23.64 | 0.0 | 0.0 | 0.0 | 23.64 | 23.64 | 23.64 | 0.0 | 0.0 |
|
| 2 | 30 | 21 May 2025 | 21 March 2025 | 195.18 | 23.64 | 0.0 | 0.0 | 0.0 | 23.64 | 23.64 | 23.64 | 0.0 | 0.0 |
|
||||||
| 3 | 31 | 21 June 2025 | 21 March 2025 | 171.54 | 23.64 | 0.0 | 0.0 | 0.0 | 23.64 | 23.64 | 23.64 | 0.0 | 0.0 |
|
| 3 | 31 | 21 June 2025 | 21 March 2025 | 171.54 | 23.64 | 0.0 | 0.0 | 0.0 | 23.64 | 23.64 | 23.64 | 0.0 | 0.0 |
|
||||||
| 4 | 30 | 21 July 2025 | 21 March 2025 | 147.9 | 23.64 | 0.0 | 0.0 | 0.0 | 23.64 | 23.64 | 23.64 | 0.0 | 0.0 |
|
| 4 | 30 | 21 July 2025 | 21 March 2025 | 147.9 | 23.64 | 0.0 | 0.0 | 0.0 | 23.64 | 23.64 | 23.64 | 0.0 | 0.0 |
|
||||||
@@ -6209,7 +6209,7 @@ Feature: LoanAccrualActivity
|
|||||||
| 12 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
| 12 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
And Loan Repayment schedule has the following data in Total row:
|
And 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 |
|
||||||
| 342.46 | 0.77 | 0.0 | 0.0 | 343.23 | 302.46 | 302.46 | 0.0 | 40.77 |
|
| 342.46 | 0.84 | 0.0 | 0.0 | 343.3 | 302.46 | 302.46 | 0.0 | 40.84 |
|
||||||
And Loan Transactions tab has the following data:
|
And Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
@@ -6759,11 +6759,11 @@ Feature: LoanAccrualActivity
|
|||||||
When Admin sets the business date to "29 April 2025"
|
When Admin sets the business date to "29 April 2025"
|
||||||
And Admin runs inline COB job for Loan
|
And Admin runs inline COB job for Loan
|
||||||
And Customer makes "AUTOPAY" repayment on "29 April 2025" with 100 EUR transaction amount
|
And Customer makes "AUTOPAY" repayment on "29 April 2025" with 100 EUR transaction amount
|
||||||
Then Loan has 1.93 outstanding amount
|
Then Loan has 2.6 outstanding amount
|
||||||
Then Loan Repayment schedule has 12 periods, with the following data for periods:
|
Then Loan Repayment schedule has 12 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 |
|
||||||
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 1 | 31 | 21 April 2025 | | 222.26 | 120.2 | 1.93 | 0.0 | 0.0 | 122.13 |120.2 | 20.2 | 100.0| 1.93 |
|
| 1 | 31 | 21 April 2025 | | 222.26 | 120.2 | 2.6 | 0.0 | 0.0 | 122.8 |120.2 | 20.2 | 100.0| 2.6 |
|
||||||
| 2 | 30 | 21 May 2025 | 21 March 2025 | 202.06 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
| 2 | 30 | 21 May 2025 | 21 March 2025 | 202.06 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
||||||
| 3 | 31 | 21 June 2025 | 21 March 2025 | 181.86 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
| 3 | 31 | 21 June 2025 | 21 March 2025 | 181.86 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
||||||
| 4 | 30 | 21 July 2025 | 21 March 2025 | 161.66 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
| 4 | 30 | 21 July 2025 | 21 March 2025 | 161.66 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
||||||
@@ -6777,7 +6777,7 @@ Feature: LoanAccrualActivity
|
|||||||
| 12 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 20.26 | 0.0 | 0.0 | 0.0 | 20.26 | 20.26 | 20.26 | 0.0 | 0.0 |
|
| 12 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 20.26 | 0.0 | 0.0 | 0.0 | 20.26 | 20.26 | 20.26 | 0.0 | 0.0 |
|
||||||
And Loan Repayment schedule has the following data in Total row:
|
And 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 |
|
||||||
| 342.46 | 1.93 | 0.0 | 0.0 | 344.39 | 342.46 | 242.46 | 100.0 | 1.93 |
|
| 342.46 | 2.6 | 0.0 | 0.0 | 345.06 | 342.46 | 242.46 | 100.0 | 2.6 |
|
||||||
And Loan Transactions tab has the following data:
|
And Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
@@ -6798,13 +6798,13 @@ Feature: LoanAccrualActivity
|
|||||||
Then Credit Balance Refund transaction on active loan "29 April 2025" with 100 EUR transaction amount will result an error
|
Then Credit Balance Refund transaction on active loan "29 April 2025" with 100 EUR transaction amount will result an error
|
||||||
When Admin sets the business date to "06 May 2025"
|
When Admin sets the business date to "06 May 2025"
|
||||||
And Admin runs inline COB job for Loan
|
And Admin runs inline COB job for Loan
|
||||||
And Customer makes "AUTOPAY" repayment on "06 May 2025" with 1.93 EUR transaction amount
|
And Customer makes "AUTOPAY" repayment on "06 May 2025" with 2.6 EUR transaction amount
|
||||||
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||||
Then Loan has 0 outstanding amount
|
Then Loan has 0 outstanding amount
|
||||||
Then Loan Repayment schedule has 12 periods, with the following data for periods:
|
Then Loan Repayment schedule has 12 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 |
|
||||||
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 1 | 31 | 21 April 2025 | 06 May 2025 | 222.26 | 120.2 | 1.93 | 0.0 | 0.0 | 122.13 |122.13 | 20.2 | 101.93 | 0.0 |
|
| 1 | 31 | 21 April 2025 | 06 May 2025 | 222.26 | 120.2 | 2.6 | 0.0 | 0.0 | 122.8 |122.8 | 20.2 | 102.6 | 0.0 |
|
||||||
| 2 | 30 | 21 May 2025 | 21 March 2025 | 202.06 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
| 2 | 30 | 21 May 2025 | 21 March 2025 | 202.06 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
||||||
| 3 | 31 | 21 June 2025 | 21 March 2025 | 181.86 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
| 3 | 31 | 21 June 2025 | 21 March 2025 | 181.86 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
||||||
| 4 | 30 | 21 July 2025 | 21 March 2025 | 161.66 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
| 4 | 30 | 21 July 2025 | 21 March 2025 | 161.66 | 20.2 | 0.0 | 0.0 | 0.0 | 20.2 | 20.2 | 20.2 | 0.0 | 0.0 |
|
||||||
@@ -6818,7 +6818,7 @@ Feature: LoanAccrualActivity
|
|||||||
| 12 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 20.26 | 0.0 | 0.0 | 0.0 | 20.26 | 20.26 | 20.26 | 0.0 | 0.0 |
|
| 12 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 20.26 | 0.0 | 0.0 | 0.0 | 20.26 | 20.26 | 20.26 | 0.0 | 0.0 |
|
||||||
And Loan Repayment schedule has the following data in Total row:
|
And 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 |
|
||||||
| 342.46 | 1.93 | 0.0 | 0.0 | 344.39 | 344.39 | 242.46 | 101.93 | 0.0 |
|
| 342.46 | 2.6 | 0.0 | 0.0 | 345.06 | 345.06 | 242.46 | 102.6 | 0.0 |
|
||||||
And Loan Transactions tab has the following data:
|
And Loan Transactions tab has the following data:
|
||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
@@ -6836,8 +6836,7 @@ Feature: LoanAccrualActivity
|
|||||||
| 28 April 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
| 28 April 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 29 April 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
| 29 April 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 29 April 2025 | Accrual | 0.09 | 0.0 | 0.09 | 0.0 | 0.0 | 0.0 | false | false |
|
| 29 April 2025 | Accrual | 0.09 | 0.0 | 0.09 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 06 May 2025 | Repayment | 1.93 | 0.0 | 1.93 | 0.0 | 0.0 | 0.0 | false | false |
|
| 06 May 2025 | Repayment | 2.6 | 0.0 | 2.6 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 06 May 2025 | Accrual Adjustment | 0.67 | 0.0 | 0.67 | 0.0 | 0.0 | 0.0 | false | false |
|
|
||||||
# - CBR on closed loan is forbidden - #
|
# - CBR on closed loan is forbidden - #
|
||||||
Then Credit Balance Refund transaction on active loan "06 May 2025" with 100 EUR transaction amount will result an error
|
Then Credit Balance Refund transaction on active loan "06 May 2025" with 100 EUR transaction amount will result an error
|
||||||
|
|
||||||
@@ -7157,4 +7156,223 @@ Feature: LoanAccrualActivity
|
|||||||
| 07 May 2025 | Accrual | 0.05 | 0.0 | 0.05 | 0.0 | 0.0 | 0.0 | false | false |
|
| 07 May 2025 | Accrual | 0.05 | 0.0 | 0.05 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 07 May 2025 | Accrual Activity | 1.18 | 0.0 | 1.18 | 0.0 | 0.0 | 0.0 | false | false |
|
| 07 May 2025 | Accrual Activity | 1.18 | 0.0 | 1.18 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
# - CBR on closed loan is forbidden - #
|
# - CBR on closed loan is forbidden - #
|
||||||
Then Credit Balance Refund transaction on active loan "07 May 2025" with 72.35 EUR transaction amount will result an error
|
Then Credit Balance Refund transaction on active loan "07 May 2025" with 72.35 EUR transaction amount will result an error
|
||||||
|
|
||||||
|
@TestRailId:C3736
|
||||||
|
Scenario: Verify that interest is calculated after last unpaid period in case of reversed repayment made before MIR and CBR for progressive loan with downpayment
|
||||||
|
When Admin sets the business date to "21 March 2025"
|
||||||
|
And Admin creates a client with random data
|
||||||
|
And Admin creates a fully customized loan with the following data:
|
||||||
|
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||||
|
| LP2_ADV_PYMNT_INTEREST_RECALCULATION_DAILY_EMI_360_30_MULTIDISBURSE_DOWNPAYMENT | 21 March 2025 | 242.46 | 29.99 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 12 | MONTHS | 1 | MONTHS | 12 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||||
|
And Admin successfully approves the loan on "21 March 2025" with "242.46" amount and expected disbursement date on "21 March 2025"
|
||||||
|
And Admin successfully disburse the loan on "21 March 2025" with "242.46" EUR transaction amount
|
||||||
|
Then Loan Repayment schedule has 13 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 |
|
||||||
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 21 March 2025 | | 181.84 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 |
|
||||||
|
| 2 | 31 | 21 April 2025 | | 168.65 | 13.19 | 4.54 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 3 | 30 | 21 May 2025 | | 155.13 | 13.52 | 4.21 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 4 | 31 | 21 June 2025 | | 141.28 | 13.85 | 3.88 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 5 | 30 | 21 July 2025 | | 127.08 | 14.2 | 3.53 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 6 | 31 | 21 August 2025 | | 112.53 | 14.55 | 3.18 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 7 | 31 | 21 September 2025 | | 97.61 | 14.92 | 2.81 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 8 | 30 | 21 October 2025 | | 82.32 | 15.29 | 2.44 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 9 | 31 | 21 November 2025 | | 66.65 | 15.67 | 2.06 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 10 | 30 | 21 December 2025 | | 50.59 | 16.06 | 1.67 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 11 | 31 | 21 January 2026 | | 34.12 | 16.47 | 1.26 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 12 | 31 | 21 February 2026 | | 17.24 | 16.88 | 0.85 | 0.0 | 0.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 |
|
||||||
|
| 13 | 28 | 21 March 2026 | | 0.0 | 17.24 | 0.43 | 0.0 | 0.0 | 17.67 | 0.0 | 0.0 | 0.0 | 17.67 |
|
||||||
|
And Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 242.46 | 30.86 | 0.0 | 0.0 | 273.32 | 0.0 | 0.0 | 0.0 | 273.32 |
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
And Customer makes "AUTOPAY" repayment on "21 March 2025" with 100 EUR transaction amount
|
||||||
|
And Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "21 March 2025" with 242.46 EUR transaction amount and system-generated Idempotency key
|
||||||
|
Then Loan status will be "OVERPAID"
|
||||||
|
Then Loan has 100 overpaid amount
|
||||||
|
Then Loan Repayment schedule has 13 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 |
|
||||||
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 21 March 2025 | 21 March 2025 | 181.84 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 | 60.62 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 21 April 2025 | 21 March 2025 | 164.11 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 3 | 30 | 21 May 2025 | 21 March 2025 | 146.38 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 21 June 2025 | 21 March 2025 | 128.65 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 5 | 30 | 21 July 2025 | 21 March 2025 | 110.92 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 6 | 31 | 21 August 2025 | 21 March 2025 | 93.19 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 7 | 31 | 21 September 2025 | 21 March 2025 | 75.46 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 8 | 30 | 21 October 2025 | 21 March 2025 | 57.73 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 9 | 31 | 21 November 2025 | 21 March 2025 | 40.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 10 | 30 | 21 December 2025 | 21 March 2025 | 22.27 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 11 | 31 | 21 January 2026 | 21 March 2025 | 4.54 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 12 | 31 | 21 February 2026 | 21 March 2025 | 0.0 | 4.54 | 0.0 | 0.0 | 0.0 | 4.54 | 4.54 | 4.54 | 0.0 | 0.0 |
|
||||||
|
| 13 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
And Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 242.46 | 0.0 | 0.0 | 0.0 | 242.46 | 242.46 | 181.84 | 0.0 | 0.0 |
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
| 21 March 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 142.46 | false | false |
|
||||||
|
| 21 March 2025 | Merchant Issued Refund | 242.46 | 142.46 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
When Admin sets the business date to "28 March 2025"
|
||||||
|
And Admin runs inline COB job for Loan
|
||||||
|
When Admin makes Credit Balance Refund transaction on "28 March 2025" with 100 EUR transaction amount
|
||||||
|
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||||
|
Then Loan has 0 outstanding amount
|
||||||
|
Then Loan Repayment schedule has 13 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 |
|
||||||
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 21 March 2025 | 21 March 2025 | 181.84 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 | 60.62 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 21 April 2025 | 21 March 2025 | 164.11 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 3 | 30 | 21 May 2025 | 21 March 2025 | 146.38 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 21 June 2025 | 21 March 2025 | 128.65 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 5 | 30 | 21 July 2025 | 21 March 2025 | 110.92 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 6 | 31 | 21 August 2025 | 21 March 2025 | 93.19 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 7 | 31 | 21 September 2025 | 21 March 2025 | 75.46 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 8 | 30 | 21 October 2025 | 21 March 2025 | 57.73 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 9 | 31 | 21 November 2025 | 21 March 2025 | 40.0 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 10 | 30 | 21 December 2025 | 21 March 2025 | 22.27 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 11 | 31 | 21 January 2026 | 21 March 2025 | 4.54 | 17.73 | 0.0 | 0.0 | 0.0 | 17.73 | 17.73 | 17.73 | 0.0 | 0.0 |
|
||||||
|
| 12 | 31 | 21 February 2026 | 21 March 2025 | 0.0 | 4.54 | 0.0 | 0.0 | 0.0 | 4.54 | 4.54 | 4.54 | 0.0 | 0.0 |
|
||||||
|
| 13 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
|
||||||
|
And Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 242.46 | 0.0 | 0.0 | 0.0 | 242.46 | 242.46 | 181.84 | 0.0 | 0.0 |
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
| 21 March 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 142.46 | false | false |
|
||||||
|
| 21 March 2025 | Merchant Issued Refund | 242.46 | 142.46 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 28 March 2025 | Credit Balance Refund | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
When Admin sets the business date to "02 April 2025"
|
||||||
|
And Admin runs inline COB job for Loan
|
||||||
|
When Customer undo "1"th "Repayment" transaction made on "21 March 2025"
|
||||||
|
Then Loan Repayment schedule has 13 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 |
|
||||||
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 21 March 2025 | 21 March 2025 | 181.84 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 | 60.62 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 21 April 2025 | | 166.69 | 115.15 | 1.93 | 0.0 | 0.0 | 117.08 | 15.15 | 15.15 | 0.0 | 101.93 |
|
||||||
|
| 3 | 30 | 21 May 2025 | 21 March 2025 | 151.54 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 21 June 2025 | 21 March 2025 | 136.39 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 5 | 30 | 21 July 2025 | 21 March 2025 | 121.24 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 6 | 31 | 21 August 2025 | 21 March 2025 | 106.09 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 7 | 31 | 21 September 2025 | 21 March 2025 | 90.94 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 8 | 30 | 21 October 2025 | 21 March 2025 | 75.79 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 9 | 31 | 21 November 2025 | 21 March 2025 | 60.64 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 10 | 30 | 21 December 2025 | 21 March 2025 | 45.49 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 11 | 31 | 21 January 2026 | 21 March 2025 | 30.34 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 12 | 31 | 21 February 2026 | 21 March 2025 | 15.19 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 13 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 15.19 | 0.0 | 0.0 | 0.0 | 15.19 | 15.19 | 15.19 | 0.0 | 0.0 |
|
||||||
|
And Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 342.46 | 1.93 | 0.0 | 0.0 | 344.39 | 242.46 | 181.84 | 0.0 | 101.93 |
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
| 21 March 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 142.46 | true | false |
|
||||||
|
| 21 March 2025 | Merchant Issued Refund | 242.46 | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||||
|
| 28 March 2025 | Credit Balance Refund | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | true |
|
||||||
|
When Admin sets the business date to "21 April 2025"
|
||||||
|
And Admin runs inline COB job for Loan
|
||||||
|
Then Loan Repayment schedule has 13 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 |
|
||||||
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 21 March 2025 | 21 March 2025 | 181.84 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 | 60.62 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 21 April 2025 | | 166.69 | 115.15 | 1.93 | 0.0 | 0.0 | 117.08 | 15.15 | 15.15 | 0.0 | 101.93 |
|
||||||
|
| 3 | 30 | 21 May 2025 | 21 March 2025 | 151.54 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 21 June 2025 | 21 March 2025 | 136.39 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 5 | 30 | 21 July 2025 | 21 March 2025 | 121.24 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 6 | 31 | 21 August 2025 | 21 March 2025 | 106.09 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 7 | 31 | 21 September 2025 | 21 March 2025 | 90.94 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 8 | 30 | 21 October 2025 | 21 March 2025 | 75.79 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 9 | 31 | 21 November 2025 | 21 March 2025 | 60.64 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 10 | 30 | 21 December 2025 | 21 March 2025 | 45.49 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 11 | 31 | 21 January 2026 | 21 March 2025 | 30.34 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 12 | 31 | 21 February 2026 | 21 March 2025 | 15.19 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 13 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 15.19 | 0.0 | 0.0 | 0.0 | 15.19 | 15.19 | 15.19 | 0.0 | 0.0 |
|
||||||
|
And Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 342.46 | 1.93 | 0.0 | 0.0 | 344.39 | 242.46 | 181.84 | 0.0 | 101.93 |
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
| 21 March 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 142.46 | true | false |
|
||||||
|
| 21 March 2025 | Merchant Issued Refund | 242.46 | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||||
|
| 28 March 2025 | Credit Balance Refund | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | true |
|
||||||
|
| 20 April 2025 | Accrual | 1.85 | 0.0 | 1.85 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
When Admin sets the business date to "22 April 2025"
|
||||||
|
And Admin runs inline COB job for Loan
|
||||||
|
Then Loan Repayment schedule has 13 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 |
|
||||||
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 21 March 2025 | 21 March 2025 | 181.84 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 | 60.62 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 21 April 2025 | | 166.69 | 115.15 | 2.01 | 0.0 | 0.0 | 117.16 | 15.15 | 15.15 | 0.0 | 102.01 |
|
||||||
|
| 3 | 30 | 21 May 2025 | 21 March 2025 | 151.54 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 21 June 2025 | 21 March 2025 | 136.39 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 5 | 30 | 21 July 2025 | 21 March 2025 | 121.24 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 6 | 31 | 21 August 2025 | 21 March 2025 | 106.09 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 7 | 31 | 21 September 2025 | 21 March 2025 | 90.94 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 8 | 30 | 21 October 2025 | 21 March 2025 | 75.79 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 9 | 31 | 21 November 2025 | 21 March 2025 | 60.64 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 10 | 30 | 21 December 2025 | 21 March 2025 | 45.49 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 11 | 31 | 21 January 2026 | 21 March 2025 | 30.34 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 12 | 31 | 21 February 2026 | 21 March 2025 | 15.19 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 13 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 15.19 | 0.0 | 0.0 | 0.0 | 15.19 | 15.19 | 15.19 | 0.0 | 0.0 |
|
||||||
|
And Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 342.46 | 2.01 | 0.0 | 0.0 | 344.47 | 242.46 | 181.84 | 0.0 | 102.01 |
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
| 21 March 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 142.46 | true | false |
|
||||||
|
| 21 March 2025 | Merchant Issued Refund | 242.46 | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||||
|
| 28 March 2025 | Credit Balance Refund | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | true |
|
||||||
|
| 20 April 2025 | Accrual | 1.85 | 0.0 | 1.85 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 21 April 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
When Admin sets the business date to "23 April 2025"
|
||||||
|
And Admin runs inline COB job for Loan
|
||||||
|
Then Loan Repayment schedule has 13 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 |
|
||||||
|
| | | 21 March 2025 | | 242.46 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
|
| 1 | 0 | 21 March 2025 | 21 March 2025 | 181.84 | 60.62 | 0.0 | 0.0 | 0.0 | 60.62 | 60.62 | 0.0 | 0.0 | 0.0 |
|
||||||
|
| 2 | 31 | 21 April 2025 | | 166.69 | 115.15 | 2.1 | 0.0 | 0.0 | 117.25 | 15.15 | 15.15 | 0.0 | 102.1 |
|
||||||
|
| 3 | 30 | 21 May 2025 | 21 March 2025 | 151.54 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 4 | 31 | 21 June 2025 | 21 March 2025 | 136.39 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 5 | 30 | 21 July 2025 | 21 March 2025 | 121.24 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 6 | 31 | 21 August 2025 | 21 March 2025 | 106.09 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 7 | 31 | 21 September 2025 | 21 March 2025 | 90.94 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 8 | 30 | 21 October 2025 | 21 March 2025 | 75.79 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 9 | 31 | 21 November 2025 | 21 March 2025 | 60.64 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 10 | 30 | 21 December 2025 | 21 March 2025 | 45.49 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 11 | 31 | 21 January 2026 | 21 March 2025 | 30.34 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 12 | 31 | 21 February 2026 | 21 March 2025 | 15.19 | 15.15 | 0.0 | 0.0 | 0.0 | 15.15 | 15.15 | 15.15 | 0.0 | 0.0 |
|
||||||
|
| 13 | 28 | 21 March 2026 | 21 March 2025 | 0.0 | 15.19 | 0.0 | 0.0 | 0.0 | 15.19 | 15.19 | 15.19 | 0.0 | 0.0 |
|
||||||
|
And Loan Repayment schedule has the following data in Total row:
|
||||||
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
|
| 342.46 | 2.1 | 0.0 | 0.0 | 344.56 | 242.46 | 181.84 | 0.0 | 102.1 |
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
| 21 March 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 142.46 | true | false |
|
||||||
|
| 21 March 2025 | Merchant Issued Refund | 242.46 | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||||
|
| 28 March 2025 | Credit Balance Refund | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | true |
|
||||||
|
| 20 April 2025 | Accrual | 1.85 | 0.0 | 1.85 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 21 April 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 22 April 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
And Customer makes "AUTOPAY" repayment on "23 April 2025" with 102.1 EUR transaction amount
|
||||||
|
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||||
|
Then Loan has 0 outstanding amount
|
||||||
|
And Loan Transactions tab has the following data:
|
||||||
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
|
| 21 March 2025 | Disbursement | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | 242.46 | false | false |
|
||||||
|
| 21 March 2025 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 142.46 | true | false |
|
||||||
|
| 21 March 2025 | Merchant Issued Refund | 242.46 | 242.46 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||||
|
| 28 March 2025 | Credit Balance Refund | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | true |
|
||||||
|
| 20 April 2025 | Accrual | 1.85 | 0.0 | 1.85 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 21 April 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 22 April 2025 | Accrual | 0.08 | 0.0 | 0.08 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 23 April 2025 | Repayment | 102.1 | 100.0 | 2.1 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
| 23 April 2025 | Accrual | 0.09 | 0.0 | 0.09 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
|||||||
+110
-41
@@ -53,6 +53,7 @@ import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanIntere
|
|||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -182,16 +183,25 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
scheduleModel.zero()).ifPresent(repaymentPeriod -> {
|
scheduleModel.zero()).ifPresent(repaymentPeriod -> {
|
||||||
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
||||||
calculateOutstandingBalance(scheduleModel);
|
calculateOutstandingBalance(scheduleModel);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, balanceCorrectionDate);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate,
|
public void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate, LocalDate transactionDate,
|
||||||
Money interestAmount) {
|
Money interestAmount) {
|
||||||
findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate).ifPresent(rp -> rp.addPaidInterestAmount(interestAmount));
|
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate);
|
||||||
|
|
||||||
|
Optional<RepaymentPeriod> latestNotLastOpenRepaymentPeriodBeforeDate = getLatestNotLastOpenRepaymentPeriodBeforeDate(scheduleModel,
|
||||||
|
transactionDate);
|
||||||
|
if (latestNotLastOpenRepaymentPeriodBeforeDate.isPresent() && repaymentPeriod.equals(latestNotLastOpenRepaymentPeriodBeforeDate)) {
|
||||||
|
calculateUnrecognizedInterestTillDateOnScheduleModelCopyAndDefer(scheduleModel,
|
||||||
|
latestNotLastOpenRepaymentPeriodBeforeDate.get(), transactionDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
repaymentPeriod.ifPresent(rp -> rp.addPaidInterestAmount(interestAmount));
|
||||||
calculateOutstandingBalance(scheduleModel);
|
calculateOutstandingBalance(scheduleModel);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, transactionDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -211,17 +221,38 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
repaymentPeriod.ifPresent(rp -> {
|
repaymentPeriod.ifPresent(rp -> {
|
||||||
// If any period total paid > calculated EMI, then set EMI to total paid -> effectively it is marked as
|
// If any period total paid > calculated EMI, then set EMI to total paid -> effectively it is marked as
|
||||||
// fully paid
|
// fully paid
|
||||||
if (transactionDateIsBefore && rp.getTotalPaidAmount().isGreaterThan(rp.getEmiPlusCreditedAmounts())) {
|
if (transactionDateIsBefore
|
||||||
|
&& rp.getTotalPaidAmount().isGreaterThan(rp.getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest())) {
|
||||||
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount()));
|
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount()));
|
||||||
} else if (transactionDateIsBefore
|
} else if (transactionDateIsBefore
|
||||||
&& rp.getTotalPaidAmount().isEqualTo(rp.getOriginalEmi().add(rp.getTotalCreditedAmount()))) {
|
&& rp.getTotalPaidAmount().isEqualTo(rp.getOriginalEmi().add(rp.getTotalCreditedAmount()))) {
|
||||||
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount()));
|
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount()));
|
||||||
}
|
}
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, balanceCorrectionDate);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<RepaymentPeriod> getLatestNotLastOpenRepaymentPeriodBeforeDate(ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||||
|
LocalDate transactionDate) {
|
||||||
|
List<RepaymentPeriod> unpaidRepaymentPeriods = scheduleModel.repaymentPeriods() //
|
||||||
|
.stream() //
|
||||||
|
.filter(rp -> !rp.isFullyPaid()) //
|
||||||
|
.toList(); //
|
||||||
|
|
||||||
|
if (CollectionUtils.isEmpty(unpaidRepaymentPeriods)
|
||||||
|
|| unpaidRepaymentPeriods.getLast().equals(scheduleModel.repaymentPeriods().getLast())) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
RepaymentPeriod latestNotLastOpenRepaymentPeriod = unpaidRepaymentPeriods.getLast();
|
||||||
|
if (DateUtils.isBefore(transactionDate, latestNotLastOpenRepaymentPeriod.getDueDate())) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(latestNotLastOpenRepaymentPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
private void addCreditedAmountsToInterestPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
private void addCreditedAmountsToInterestPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||||
Money creditedPrincipalAmount, Money creditedInterestAmount) {
|
Money creditedPrincipalAmount, Money creditedInterestAmount) {
|
||||||
scheduleModel.repaymentPeriods().stream().filter(checkRepaymentPeriodIsInCreditRange(scheduleModel, transactionDate)).findFirst()
|
scheduleModel.repaymentPeriods().stream().filter(checkRepaymentPeriodIsInCreditRange(scheduleModel, transactionDate)).findFirst()
|
||||||
@@ -260,7 +291,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
addCreditedAmountsToInterestPeriod(scheduleModel, transactionDate, creditedPrincipalAmount, creditedInterestAmount);
|
addCreditedAmountsToInterestPeriod(scheduleModel, transactionDate, creditedPrincipalAmount, creditedInterestAmount);
|
||||||
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
||||||
calculateOutstandingBalance(scheduleModel);
|
calculateOutstandingBalance(scheduleModel);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, transactionDate);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,22 +365,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
public OutstandingDetails getOutstandingAmountsTillDate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate targetDate) {
|
public OutstandingDetails getOutstandingAmountsTillDate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate targetDate) {
|
||||||
MathContext mc = scheduleModel.mc();
|
MathContext mc = scheduleModel.mc();
|
||||||
ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.deepCopy(mc);
|
ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.deepCopy(mc);
|
||||||
// TODO use findInterestPeriod
|
calculateRateFactorForScheduleTillDateInclusive(scheduleModelCopy, targetDate);
|
||||||
scheduleModelCopy.repaymentPeriods().stream()//
|
|
||||||
.filter(rp -> targetDate.isAfter(rp.getFromDate()) && !targetDate.isAfter(rp.getDueDate())).findFirst()//
|
|
||||||
.flatMap(rp -> rp.getInterestPeriods().stream()//
|
|
||||||
.filter(ip -> targetDate.isAfter(ip.getFromDate()) && !targetDate.isAfter(ip.getDueDate())) //
|
|
||||||
.reduce((one, two) -> two))
|
|
||||||
.ifPresent(ip -> ip.setDueDate(targetDate)); //
|
|
||||||
|
|
||||||
calculateRateFactorForPeriods(scheduleModelCopy.repaymentPeriods(), scheduleModelCopy);
|
|
||||||
scheduleModelCopy.repaymentPeriods()
|
|
||||||
.forEach(rp -> rp.getInterestPeriods().stream().filter(ip -> targetDate.isBefore(ip.getDueDate())).forEach(ip -> {
|
|
||||||
ip.setRateFactor(BigDecimal.ZERO);
|
|
||||||
ip.setRateFactorTillPeriodDueDate(BigDecimal.ZERO);
|
|
||||||
}));
|
|
||||||
calculateOutstandingBalance(scheduleModelCopy);
|
calculateOutstandingBalance(scheduleModelCopy);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModelCopy);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModelCopy, targetDate);
|
||||||
|
|
||||||
Money totalOutstandingPrincipal = MathUtil
|
Money totalOutstandingPrincipal = MathUtil
|
||||||
.negativeToZero(scheduleModelCopy.getTotalDuePrincipal().minus(scheduleModelCopy.getTotalPaidPrincipal()));
|
.negativeToZero(scheduleModelCopy.getTotalDuePrincipal().minus(scheduleModelCopy.getTotalPaidPrincipal()));
|
||||||
@@ -421,7 +439,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
});
|
});
|
||||||
calculateRateFactorForPeriods(scheduleModelCopy.repaymentPeriods(), scheduleModelCopy);
|
calculateRateFactorForPeriods(scheduleModelCopy.repaymentPeriods(), scheduleModelCopy);
|
||||||
calculateOutstandingBalance(scheduleModelCopy);
|
calculateOutstandingBalance(scheduleModelCopy);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModelCopy);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModelCopy, targetDate);
|
||||||
}
|
}
|
||||||
return scheduleModelCopy;
|
return scheduleModelCopy;
|
||||||
}
|
}
|
||||||
@@ -443,37 +461,63 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
calculateEMIOnNewModelAndMerge(relatedRepaymentPeriods, scheduleModel, operation);
|
calculateEMIOnNewModelAndMerge(relatedRepaymentPeriods, scheduleModel, operation);
|
||||||
}
|
}
|
||||||
calculateOutstandingBalance(scheduleModel);
|
calculateOutstandingBalance(scheduleModel);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, calculateFromRepaymentPeriodDueDate);
|
||||||
if (onlyOnActualModelShouldApply && (scheduleModel.loanTermVariations() == null
|
if (onlyOnActualModelShouldApply && (scheduleModel.loanTermVariations() == null
|
||||||
|| scheduleModel.loanTermVariations().get(LoanTermVariationType.DUE_DATE) == null)) {
|
|| scheduleModel.loanTermVariations().get(LoanTermVariationType.DUE_DATE) == null)) {
|
||||||
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, relatedRepaymentPeriods);
|
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, relatedRepaymentPeriods);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel) {
|
private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate tillDate) {
|
||||||
MathContext mc = scheduleModel.mc();
|
|
||||||
Money totalDueInterest = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest).reduce(scheduleModel.zero(),
|
|
||||||
(m1, m2) -> m1.plus(m2, mc)); // 1.46
|
|
||||||
Money totalEMI = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getEmiPlusCreditedAmounts)
|
|
||||||
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 101.48
|
|
||||||
Money totalDisbursedAmount = scheduleModel.repaymentPeriods().stream()
|
|
||||||
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount))
|
|
||||||
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 100
|
|
||||||
Money totalCapitalizedIncome = scheduleModel.repaymentPeriods().stream()
|
|
||||||
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getCapitalizedIncomePrincipal))
|
|
||||||
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 100
|
|
||||||
|
|
||||||
Money diff = totalDisbursedAmount.plus(totalCapitalizedIncome, mc).plus(scheduleModel.getTotalCreditedPrincipal(), mc)
|
|
||||||
.plus(totalDueInterest, mc).minus(totalEMI, mc);
|
|
||||||
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
|
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
|
||||||
.reduce((first, second) -> second);
|
.reduce((first, second) -> second);
|
||||||
|
|
||||||
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> {
|
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> {
|
||||||
|
repaymentPeriod.setFutureUnrecognizedInterest(scheduleModel.zero());
|
||||||
|
scheduleModel.repaymentPeriods().forEach(rp -> {
|
||||||
|
rp.setInterestMoved(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
MathContext mc = scheduleModel.mc();
|
||||||
|
Money totalDueInterest = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest)
|
||||||
|
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 1.46
|
||||||
|
Money totalEMI = scheduleModel.repaymentPeriods().stream()
|
||||||
|
.map(RepaymentPeriod::getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest)
|
||||||
|
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 101.48
|
||||||
|
Money totalDisbursedAmount = scheduleModel.repaymentPeriods().stream()
|
||||||
|
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount))
|
||||||
|
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 100
|
||||||
|
Money totalCapitalizedIncome = scheduleModel.repaymentPeriods().stream()
|
||||||
|
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getCapitalizedIncomePrincipal))
|
||||||
|
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 100
|
||||||
|
|
||||||
|
Money diff = totalDisbursedAmount.plus(totalCapitalizedIncome, mc).plus(scheduleModel.getTotalCreditedPrincipal(), mc)
|
||||||
|
.plus(totalDueInterest, mc).minus(totalEMI, mc);
|
||||||
|
|
||||||
repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff, mc));
|
repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff, mc));
|
||||||
if (repaymentPeriod.getEmi()
|
if (repaymentPeriod.getEmi()
|
||||||
.isLessThan(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc))) {
|
.isLessThan(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc))) {
|
||||||
repaymentPeriod.setEmi(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc));
|
repaymentPeriod.setEmi(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc));
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, tillDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateUnrecognizedInterestTillDateOnScheduleModelCopyAndDefer(scheduleModel, repaymentPeriod, tillDate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateUnrecognizedInterestTillDateOnScheduleModelCopyAndDefer(ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||||
|
RepaymentPeriod repaymentPeriod, LocalDate tillDate) {
|
||||||
|
MathContext mc = scheduleModel.mc();
|
||||||
|
ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.deepCopy(mc);
|
||||||
|
calculateRateFactorForScheduleTillDateInclusive(scheduleModelCopy, tillDate);
|
||||||
|
Optional<RepaymentPeriod> futureUnrecognizedInterestPeriod = getPeriodWithUnrecognizedInterest(repaymentPeriod, scheduleModelCopy);
|
||||||
|
|
||||||
|
futureUnrecognizedInterestPeriod.ifPresent(period -> {
|
||||||
|
repaymentPeriod.setFutureUnrecognizedInterest(period.getUnrecognizedInterest());
|
||||||
|
scheduleModel.repaymentPeriods().stream().filter(rp -> rp.getDueDate().isAfter(repaymentPeriod.getDueDate())) //
|
||||||
|
.forEach(rp -> {
|
||||||
|
rp.setInterestMoved(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +553,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
calculateOutstandingBalance(newScheduleModel);
|
calculateOutstandingBalance(newScheduleModel);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(newScheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(newScheduleModel, relatedPeriodsFirstDueDate);
|
||||||
if (!getEmiAdjustment(newScheduleModel.repaymentPeriods()).hasLessEmiDifference(emiAdjustment)) {
|
if (!getEmiAdjustment(newScheduleModel.repaymentPeriods()).hasLessEmiDifference(emiAdjustment)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -861,6 +905,31 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
return new EmiAdjustment(repaymentPeriods.get(0).getEmi(), repaymentPeriods.get(0).getEmi().copy(0.0), repaymentPeriods, 0);
|
return new EmiAdjustment(repaymentPeriods.get(0).getEmi(), repaymentPeriods.get(0).getEmi().copy(0.0), repaymentPeriods, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void calculateRateFactorForScheduleTillDateInclusive(ProgressiveLoanInterestScheduleModel scheduleModelCopy,
|
||||||
|
LocalDate targetDate) {
|
||||||
|
scheduleModelCopy.findRepaymentPeriod(targetDate).flatMap(rp -> rp.findInterestPeriod(targetDate))
|
||||||
|
.ifPresent(ip -> ip.setDueDate(targetDate));
|
||||||
|
|
||||||
|
calculateRateFactorForPeriods(scheduleModelCopy.repaymentPeriods(), scheduleModelCopy);
|
||||||
|
|
||||||
|
scheduleModelCopy.repaymentPeriods()
|
||||||
|
.forEach(rp -> rp.getInterestPeriods().stream().filter(ip -> targetDate.isBefore(ip.getDueDate())).forEach(ip -> {
|
||||||
|
ip.setRateFactor(BigDecimal.ZERO);
|
||||||
|
ip.setRateFactorTillPeriodDueDate(BigDecimal.ZERO);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<RepaymentPeriod> getPeriodWithUnrecognizedInterest(RepaymentPeriod lastUnpaidRepaymentPeriod,
|
||||||
|
ProgressiveLoanInterestScheduleModel scheduleModelCopy) {
|
||||||
|
for (RepaymentPeriod period : scheduleModelCopy.repaymentPeriods().reversed()) {
|
||||||
|
if (MathUtil.isGreaterThanZero(period.getUnrecognizedInterest())
|
||||||
|
&& period.getDueDate().isAfter(lastUnpaidRepaymentPeriod.getDueDate())) {
|
||||||
|
return Optional.of(period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate Rate Factor Product from rate factors
|
* Calculate Rate Factor Product from rate factors
|
||||||
*/
|
*/
|
||||||
@@ -1082,7 +1151,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
final List<RepaymentPeriod> relatedRepaymentPeriods = scheduleModel.getRelatedRepaymentPeriods(startDate);
|
final List<RepaymentPeriod> relatedRepaymentPeriods = scheduleModel.getRelatedRepaymentPeriods(startDate);
|
||||||
calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
|
calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
|
||||||
calculateOutstandingBalance(scheduleModel);
|
calculateOutstandingBalance(scheduleModel);
|
||||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, startDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getUncountablePeriods(final List<RepaymentPeriod> relatedRepaymentPeriods, final Money originalEmi) {
|
private long getUncountablePeriods(final List<RepaymentPeriod> relatedRepaymentPeriods, final Money originalEmi) {
|
||||||
|
|||||||
+1
-1
@@ -145,7 +145,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
|||||||
.multiply(getRateFactorTillPeriodDueDate(), mc) //
|
.multiply(getRateFactorTillPeriodDueDate(), mc) //
|
||||||
.divide(BigDecimal.valueOf(lengthTillPeriodDueDate), mc) //
|
.divide(BigDecimal.valueOf(lengthTillPeriodDueDate), mc) //
|
||||||
.multiply(BigDecimal.valueOf(getLength()), mc); //
|
.multiply(BigDecimal.valueOf(getLength()), mc); //
|
||||||
return MathUtil.negativeToZero(MathUtil.add(creditedInterest.getAmount(), interestDueTillRepaymentDueDate, mc));
|
return MathUtil.negativeToZero(MathUtil.add(mc, creditedInterest.getAmount(), interestDueTillRepaymentDueDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLength() {
|
public long getLength() {
|
||||||
|
|||||||
+33
-20
@@ -60,6 +60,9 @@ public final class RepaymentPeriod {
|
|||||||
private Money paidPrincipal;
|
private Money paidPrincipal;
|
||||||
@Getter
|
@Getter
|
||||||
private Money paidInterest;
|
private Money paidInterest;
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private Money futureUnrecognizedInterest;
|
||||||
|
|
||||||
@JsonExclude
|
@JsonExclude
|
||||||
private final MathContext mc;
|
private final MathContext mc;
|
||||||
@@ -72,9 +75,12 @@ public final class RepaymentPeriod {
|
|||||||
private Memo<Money> dueInterestCalculation;
|
private Memo<Money> dueInterestCalculation;
|
||||||
@JsonExclude
|
@JsonExclude
|
||||||
private Memo<Money> outstandingBalanceCalculation;
|
private Memo<Money> outstandingBalanceCalculation;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private boolean isInterestMoved = false;
|
||||||
|
|
||||||
private RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, List<InterestPeriod> interestPeriods,
|
private RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, List<InterestPeriod> interestPeriods,
|
||||||
Money emi, Money originalEmi, Money paidPrincipal, Money paidInterest, MathContext mc) {
|
Money emi, Money originalEmi, Money paidPrincipal, Money paidInterest, Money futureUnrecognizedInterest, MathContext mc) {
|
||||||
this.previous = previous;
|
this.previous = previous;
|
||||||
this.fromDate = fromDate;
|
this.fromDate = fromDate;
|
||||||
this.dueDate = dueDate;
|
this.dueDate = dueDate;
|
||||||
@@ -83,17 +89,18 @@ public final class RepaymentPeriod {
|
|||||||
this.originalEmi = originalEmi;
|
this.originalEmi = originalEmi;
|
||||||
this.paidPrincipal = paidPrincipal;
|
this.paidPrincipal = paidPrincipal;
|
||||||
this.paidInterest = paidInterest;
|
this.paidInterest = paidInterest;
|
||||||
|
this.futureUnrecognizedInterest = futureUnrecognizedInterest;
|
||||||
this.mc = mc;
|
this.mc = mc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RepaymentPeriod empty(RepaymentPeriod previous, MathContext mc) {
|
public static RepaymentPeriod empty(RepaymentPeriod previous, MathContext mc) {
|
||||||
return new RepaymentPeriod(previous, null, null, new ArrayList<>(), null, null, null, null, mc);
|
return new RepaymentPeriod(previous, null, null, new ArrayList<>(), null, null, null, null, null, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RepaymentPeriod create(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, Money emi, MathContext mc) {
|
public static RepaymentPeriod create(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, Money emi, MathContext mc) {
|
||||||
final Money zero = emi.zero();
|
final Money zero = emi.zero();
|
||||||
final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, fromDate, dueDate, new ArrayList<>(), emi, emi, zero, zero,
|
final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, fromDate, dueDate, new ArrayList<>(), emi, emi, zero, zero,
|
||||||
mc);
|
zero, mc);
|
||||||
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
||||||
newRepaymentPeriod.interestPeriods.add(InterestPeriod.withEmptyAmounts(newRepaymentPeriod, fromDate, dueDate));
|
newRepaymentPeriod.interestPeriods.add(InterestPeriod.withEmptyAmounts(newRepaymentPeriod, fromDate, dueDate));
|
||||||
return newRepaymentPeriod;
|
return newRepaymentPeriod;
|
||||||
@@ -102,7 +109,7 @@ public final class RepaymentPeriod {
|
|||||||
public static RepaymentPeriod copy(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod, MathContext mc) {
|
public static RepaymentPeriod copy(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod, MathContext mc) {
|
||||||
final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, repaymentPeriod.fromDate, repaymentPeriod.dueDate,
|
final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, repaymentPeriod.fromDate, repaymentPeriod.dueDate,
|
||||||
new ArrayList<>(), repaymentPeriod.emi, repaymentPeriod.originalEmi, repaymentPeriod.paidPrincipal,
|
new ArrayList<>(), repaymentPeriod.emi, repaymentPeriod.originalEmi, repaymentPeriod.paidPrincipal,
|
||||||
repaymentPeriod.paidInterest, mc);
|
repaymentPeriod.paidInterest, repaymentPeriod.futureUnrecognizedInterest, mc);
|
||||||
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
||||||
for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
|
for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
|
||||||
newRepaymentPeriod.interestPeriods.add(InterestPeriod.copy(newRepaymentPeriod, interestPeriod, mc));
|
newRepaymentPeriod.interestPeriods.add(InterestPeriod.copy(newRepaymentPeriod, interestPeriod, mc));
|
||||||
@@ -113,7 +120,7 @@ public final class RepaymentPeriod {
|
|||||||
public static RepaymentPeriod copyWithoutPaidAmounts(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod, MathContext mc) {
|
public static RepaymentPeriod copyWithoutPaidAmounts(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod, MathContext mc) {
|
||||||
final Money zero = repaymentPeriod.emi.zero();
|
final Money zero = repaymentPeriod.emi.zero();
|
||||||
final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, repaymentPeriod.fromDate, repaymentPeriod.dueDate,
|
final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, repaymentPeriod.fromDate, repaymentPeriod.dueDate,
|
||||||
new ArrayList<>(), repaymentPeriod.emi, repaymentPeriod.originalEmi, zero, zero, mc);
|
new ArrayList<>(), repaymentPeriod.emi, repaymentPeriod.originalEmi, zero, zero, zero, mc);
|
||||||
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
// There is always at least 1 interest period, by default with same from-due date as repayment period
|
||||||
for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
|
for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
|
||||||
var interestPeriodCopy = InterestPeriod.copy(newRepaymentPeriod, interestPeriod);
|
var interestPeriodCopy = InterestPeriod.copy(newRepaymentPeriod, interestPeriod);
|
||||||
@@ -154,14 +161,19 @@ public final class RepaymentPeriod {
|
|||||||
public Money getCalculatedDueInterest() {
|
public Money getCalculatedDueInterest() {
|
||||||
if (calculatedDueInterestCalculation == null) {
|
if (calculatedDueInterestCalculation == null) {
|
||||||
calculatedDueInterestCalculation = Memo.of(this::calculateCalculatedDueInterest,
|
calculatedDueInterestCalculation = Memo.of(this::calculateCalculatedDueInterest,
|
||||||
() -> new Object[] { this.previous, this.interestPeriods });
|
() -> new Object[] { this.previous, this.interestPeriods, this.futureUnrecognizedInterest, this.isInterestMoved });
|
||||||
}
|
}
|
||||||
return calculatedDueInterestCalculation.get();
|
return calculatedDueInterestCalculation.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money calculateCalculatedDueInterest() {
|
private Money calculateCalculatedDueInterest() {
|
||||||
Money calculatedDueInterest = Money.of(emi.getCurrencyData(),
|
Money calculatedDueInterest = getZero(mc);
|
||||||
getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest).reduce(BigDecimal.ZERO, BigDecimal::add), mc);
|
if (!isInterestMoved) {
|
||||||
|
calculatedDueInterest = Money.of(emi.getCurrencyData(),
|
||||||
|
getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest).reduce(BigDecimal.ZERO, BigDecimal::add),
|
||||||
|
mc);
|
||||||
|
}
|
||||||
|
calculatedDueInterest = calculatedDueInterest.add(getFutureUnrecognizedInterest(), mc);
|
||||||
if (getPrevious().isPresent()) {
|
if (getPrevious().isPresent()) {
|
||||||
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest(), mc);
|
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest(), mc);
|
||||||
}
|
}
|
||||||
@@ -176,22 +188,22 @@ public final class RepaymentPeriod {
|
|||||||
public Money getDueInterest() {
|
public Money getDueInterest() {
|
||||||
if (dueInterestCalculation == null) {
|
if (dueInterestCalculation == null) {
|
||||||
// Due interest might be the maximum paid if there is pay-off or early repayment
|
// Due interest might be the maximum paid if there is pay-off or early repayment
|
||||||
dueInterestCalculation = Memo
|
dueInterestCalculation = Memo.of(
|
||||||
.of(() -> MathUtil.max(
|
() -> MathUtil.max(getPaidPrincipal().isGreaterThan(getCalculatedDuePrincipal()) ? getPaidInterest()
|
||||||
getPaidPrincipal().isGreaterThan(getCalculatedDuePrincipal()) ? getPaidInterest()
|
: MathUtil.min(getCalculatedDueInterest(), getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest(), false),
|
||||||
: MathUtil.min(getCalculatedDueInterest(), getEmiPlusCreditedAmounts(), false),
|
getPaidInterest(), false),
|
||||||
getPaidInterest(), false), () -> new Object[] { paidPrincipal, paidInterest, interestPeriods });
|
() -> new Object[] { paidPrincipal, paidInterest, interestPeriods, futureUnrecognizedInterest });
|
||||||
}
|
}
|
||||||
return dueInterestCalculation.get();
|
return dueInterestCalculation.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives back an EMI amount which includes credited amounts as well
|
* Gives back an EMI amount which includes credited amounts and future unrecognized interest as well
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Money getEmiPlusCreditedAmounts() {
|
public Money getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest() {
|
||||||
return getEmi().plus(getTotalCreditedAmount(), mc); //
|
return getEmi().plus(getTotalCreditedAmount(), mc).plus(getFutureUnrecognizedInterest(), mc); //
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,7 +212,7 @@ public final class RepaymentPeriod {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Money getCalculatedDuePrincipal() {
|
public Money getCalculatedDuePrincipal() {
|
||||||
return MathUtil.negativeToZero(getEmiPlusCreditedAmounts().minus(getCalculatedDueInterest(), mc), mc);
|
return MathUtil.negativeToZero(getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest().minus(getCalculatedDueInterest(), mc), mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -243,8 +255,9 @@ public final class RepaymentPeriod {
|
|||||||
*/
|
*/
|
||||||
public Money getDuePrincipal() {
|
public Money getDuePrincipal() {
|
||||||
// Due principal might be the maximum paid if there is pay-off or early repayment
|
// Due principal might be the maximum paid if there is pay-off or early repayment
|
||||||
return MathUtil.max(MathUtil.negativeToZero(getEmiPlusCreditedAmounts().minus(getDueInterest(), mc), mc), getPaidPrincipal(),
|
return MathUtil.max(
|
||||||
false);
|
MathUtil.negativeToZero(getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest().minus(getDueInterest(), mc), mc),
|
||||||
|
getPaidPrincipal(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -266,7 +279,7 @@ public final class RepaymentPeriod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFullyPaid() {
|
public boolean isFullyPaid() {
|
||||||
return getEmiPlusCreditedAmounts().isEqualTo(getTotalPaidAmount());
|
return getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest().isEqualTo(getTotalPaidAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user