This is an automated email from the ASF dual-hosted git repository. arnold pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 2858316c71120e43ad97c0f986f10dccacb7d636 Author: Oleksii Novikov <[email protected]> AuthorDate: Wed Dec 17 15:22:54 2025 +0200 FINERACT-2413: Improve re-amortization undo validation --- .../stepdef/loan/LoanReAmortizationStepDef.java | 2 +- .../resources/features/LoanReAmortization.feature | 171 ++++++++++++++++++++- .../reamortization/LoanReAmortizationService.java | 14 +- .../LoanReAmortizationValidator.java | 29 +--- .../LoanReAmortizationValidatorTest.java | 45 +----- 5 files changed, 182 insertions(+), 79 deletions(-) diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAmortizationStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAmortizationStepDef.java index 615080f148..9ded65b563 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAmortizationStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAmortizationStepDef.java @@ -93,7 +93,7 @@ public class LoanReAmortizationStepDef extends AbstractStepDef { testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response); } - @When("When Admin undo Loan re-amortization transaction on current business date") + @When("Admin undo Loan re-amortization transaction on current business date") public void undoLoanReAmortization() { PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); long loanId = loanResponse.getLoanId(); diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanReAmortization.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanReAmortization.feature index c329f24c30..488464ac53 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanReAmortization.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanReAmortization.feature @@ -113,7 +113,7 @@ Feature: LoanReAmortization | 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 | false | | 25 January 2024 | Re-amortize | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | When Admin sets the business date to "26 January 2024" - When When Admin undo Loan re-amortization transaction on current business date + When Admin undo Loan re-amortization transaction on current business date Then Loan Repayment schedule has 4 periods, with the following data for periods: | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | @@ -673,7 +673,7 @@ Feature: LoanReAmortization | 21 February 2024 | Down Payment | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 675.0 | true | false | # --- Undo re-amortization --- When Admin sets the business date to "23 February 2024" - And When Admin undo Loan re-amortization transaction on current business date + And Admin undo Loan re-amortization transaction on current business date Then Loan Repayment schedule has 7 periods, with the following data for periods: | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | | | 01 January 2024 | | 800.0 | | | 0.0 | | 0.0 | 0.0 | | | | @@ -2002,4 +2002,169 @@ Feature: LoanReAmortization | 15 March 2024 | Re-amortize | 4.02 | 3.61 | 0.41 | 0.0 | 0.0 | 0.0 | false | false | When Loan Pay-off is made on "15 March 2024" - Then Loan is closed with zero outstanding balance and it's all installments have obligations met \ No newline at end of file + Then Loan is closed with zero outstanding balance and it's all installments have obligations met + + Scenario: Verify Re-amortization reversal on interest bearing loan - Interest calculation: Default Behavior - UC8 + When Admin sets the business date to "01 January 2024" + When Admin creates a client with random data + When Admin creates a fully customized loan with the following data: + | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy | + | LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_NO_CALC_ON_PAST_DUE_TILL_PRECLOSE | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024" + When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | + When Admin sets the business date to "01 February 2024" + And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 17.01 | 0.0 | 0.0 | 85.04 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false | + When Admin sets the business date to "15 March 2024" + And Admin creates a Loan re-amortization transaction on current business date + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 | + | 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | + | 3 | 31 | 01 April 2024 | | 63.23 | 20.34 | 0.98 | 0.0 | 0.0 | 21.32 | 0.0 | 0.0 | 0.0 | 21.32 | + | 4 | 30 | 01 May 2024 | | 42.28 | 20.95 | 0.37 | 0.0 | 0.0 | 21.32 | 0.0 | 0.0 | 0.0 | 21.32 | + | 5 | 31 | 01 June 2024 | | 21.21 | 21.07 | 0.25 | 0.0 | 0.0 | 21.32 | 0.0 | 0.0 | 0.0 | 21.32 | + | 6 | 30 | 01 July 2024 | | 0.0 | 21.21 | 0.12 | 0.0 | 0.0 | 21.33 | 0.0 | 0.0 | 0.0 | 21.33 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.3 | 0.0 | 0.0 | 102.3 | 17.01 | 0.0 | 0.0 | 85.29 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false | + | 15 March 2024 | Re-amortize | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 0.0 | false | false | + When Admin sets the business date to "1 April 2024" + When Admin undo Loan re-amortization transaction on current business date + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 17.01 | 0.0 | 0.0 | 85.04 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false | + | 15 March 2024 | Re-amortize | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 0.0 | true | false | + + When Loan Pay-off is made on "15 March 2024" + Then Loan is closed with zero outstanding balance and it's all installments have obligations met + + Scenario: Verify Re-amortization reversal on interest bearing loan - Interest handling: EQUAL_AMORTIZATION_INTEREST_SPLIT - UC8 + When Admin sets the business date to "01 January 2024" + 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_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_NO_CALC_ON_PAST_DUE_TILL_PRECLOSE | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024" + And Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 | + And Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | +# --- Repayment on due date --- + When Admin sets the business date to "01 February 2024" + And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 17.01 | 0.0 | 0.0 | 85.04 | + And Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false | +# --- Re-amortization transaction --- + When Admin sets the business date to "15 March 2024" + And Admin creates a Loan re-amortization transaction on current business date with reAmortizationInterestHandling "EQUAL_AMORTIZATION_INTEREST_SPLIT" + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 | + | 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | + | 3 | 31 | 01 April 2024 | | 62.86 | 20.71 | 0.61 | 0.0 | 0.0 | 21.32 | 0.0 | 0.0 | 0.0 | 21.32 | + | 4 | 30 | 01 May 2024 | | 42.03 | 20.83 | 0.49 | 0.0 | 0.0 | 21.32 | 0.0 | 0.0 | 0.0 | 21.32 | + | 5 | 31 | 01 June 2024 | | 21.08 | 20.95 | 0.37 | 0.0 | 0.0 | 21.32 | 0.0 | 0.0 | 0.0 | 21.32 | + | 6 | 30 | 01 July 2024 | | 0.0 | 21.08 | 0.24 | 0.0 | 0.0 | 21.32 | 0.0 | 0.0 | 0.0 | 21.32 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.29 | 0.0 | 0.0 | 102.29 | 17.01 | 0.0 | 0.0 | 85.28 | + And Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false | + | 15 March 2024 | Re-amortize | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 0.0 | false | false | + When Admin sets the business date to "1 April 2024" + When Admin undo Loan re-amortization transaction on current business date + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 17.01 | 0.0 | 0.0 | 85.04 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false | + | 15 March 2024 | Re-amortize | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 0.0 | true | false | \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationService.java index 5a656a3af7..1311e0deb1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationService.java @@ -115,16 +115,12 @@ public class LoanReAmortizationService { public CommandProcessingResult undoReAmortize(Long loanId, JsonCommand command) { Loan loan = loanAssembler.assembleFrom(loanId); - reAmortizationValidator.validateUndoReAmortize(loan, command); + final LoanTransaction reAmortizeTransaction = reAmortizationValidator.findAndValidateReAmortizeTransactionForUndo(loan); Map<String, Object> changes = new LinkedHashMap<>(); changes.put(LoanReAmortizationApiConstants.localeParameterName, command.locale()); changes.put(LoanReAmortizationApiConstants.dateFormatParameterName, command.dateFormat()); - LoanTransaction reAmortizeTransaction = findLatestNonReversedReAmortizeTransaction(loan); - if (reAmortizeTransaction == null) { - // TODO: when validations implemented; throw exception if there isn't a reamortize transaction available - } if (loan.isProgressiveSchedule()) { loanScheduleService.regenerateRepaymentSchedule(loan); } @@ -185,14 +181,6 @@ public class LoanReAmortizationService { reprocessLoanTransactionsService.reprocessTransactions(loan); } - private LoanTransaction findLatestNonReversedReAmortizeTransaction(Loan loan) { - return loan.getLoanTransactions().stream() // - .filter(LoanTransaction::isNotReversed) // - .filter(LoanTransaction::isReAmortize) // - .max(Comparator.comparing(LoanTransaction::getTransactionDate)) // - .orElse(null); - } - private LoanTransaction createReAmortizeTransaction(Loan loan, JsonCommand command) { ExternalId txExternalId = externalIdFactory.createFromCommand(command, LoanReAmortizationApiConstants.externalIdParameterName); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java index 511146d148..2c7afbbce4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidator.java @@ -21,7 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.service.reamortization; import static org.apache.fineract.infrastructure.core.service.DateUtils.getBusinessLocalDate; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -39,7 +38,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.reamortization.LoanReAmortizationInterestHandlingType; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; -import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ChangeOperation; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.springframework.stereotype.Component; @@ -123,26 +121,18 @@ public class LoanReAmortizationValidator { } } - public void validateUndoReAmortize(Loan loan, JsonCommand command) { - validateUndoReAmortizeBusinessRules(loan); - } - - private void validateUndoReAmortizeBusinessRules(Loan loan) { - // validate if there's a reamortization transaction already - Optional<LoanTransaction> optionalReAmortizationTx = loan.getLoanTransactions().stream().filter(tx -> tx.getTypeOf().isReAmortize()) - .min(Comparator.comparing(LoanTransaction::getTransactionDate)); + public LoanTransaction findAndValidateReAmortizeTransactionForUndo(Loan loan) { + // validate if there's a non-reversed reamortization transaction already + final Optional<LoanTransaction> optionalReAmortizationTx = loan.getLoanTransactions().stream() // + .filter(LoanTransaction::isNotReversed) // + .filter(tx -> tx.getTypeOf().isReAmortize()) // + .findAny(); if (optionalReAmortizationTx.isEmpty()) { throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.reamortization.transaction.missing", - "Undoing a reamortization can only be done if there was a reamortization already", loan.getId()); + "Undoing a reamortization can only be done if there was a non-reversed reamortization already", loan.getId()); } - // validate if there's no payment between the reamortization and today - boolean repaymentExistsAfterReAmortization = loan.getLoanTransactions().stream().anyMatch(tx -> tx.getTypeOf().isRepaymentType() - && !tx.isReversed() && transactionHappenedAfterOther(tx, optionalReAmortizationTx.get())); - if (repaymentExistsAfterReAmortization) { - throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.repayment.exists.after.reamortization", - "Undoing a reamortization can only be done if there hasn't been any repayment afterwards", loan.getId()); - } + return optionalReAmortizationTx.get(); } private void throwExceptionIfValidationErrorsExist(List<ApiParameterError> dataValidationErrors) { @@ -152,7 +142,4 @@ public class LoanReAmortizationValidator { } } - private boolean transactionHappenedAfterOther(LoanTransaction transaction, LoanTransaction otherTransaction) { - return new ChangeOperation(transaction).compareTo(new ChangeOperation(otherTransaction)) > 0; - } } diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java index d84219a478..d5c1441adb 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationValidatorTest.java @@ -185,53 +185,16 @@ class LoanReAmortizationValidatorTest { List<LoanTransaction> transactions = List.of(loanTransaction(LoanTransactionType.DISBURSEMENT, actualDate.minusDays(3))); Loan loan = loan(); given(loan.getLoanTransactions()).willReturn(transactions); - JsonCommand command = jsonCommand(); // when GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class, - () -> underTest.validateUndoReAmortize(loan, command)); + () -> underTest.findAndValidateReAmortizeTransactionForUndo(loan)); // then assertThat(result).isNotNull(); assertThat(result.getGlobalisationMessageCode()).isEqualTo("error.msg.loan.reamortize.reamortization.transaction.missing"); } @Test - public void testValidateUndoReAmortize_ShouldThrowException_WhenLoanAlreadyHasRepaymentAfterReAmortization() { - // given - List<LoanTransaction> transactions = List.of(loanTransaction(LoanTransactionType.DISBURSEMENT, actualDate.minusDays(3)), - loanTransaction(LoanTransactionType.REAMORTIZE, actualDate.minusDays(2)), - loanTransaction(LoanTransactionType.REPAYMENT, actualDate.minusDays(1))); - Loan loan = loan(); - given(loan.getLoanTransactions()).willReturn(transactions); - JsonCommand command = jsonCommand(); - // when - GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class, - () -> underTest.validateUndoReAmortize(loan, command)); - // then - assertThat(result).isNotNull(); - assertThat(result.getGlobalisationMessageCode()).isEqualTo("error.msg.loan.reamortize.repayment.exists.after.reamortization"); - } - - @Test - public void testValidateUndoReAmortize_ShouldThrowException_WhenLoanAlreadyHasRepaymentAfterReAmortization_SameDay() { - // given - List<LoanTransaction> transactions = List.of(loanTransaction(LoanTransactionType.DISBURSEMENT, actualDate.minusDays(2)), - loanTransaction(LoanTransactionType.REAMORTIZE, actualDate.minusDays(1), - OffsetDateTime.of(actualDate, LocalTime.of(10, 0), ZoneOffset.UTC)), - loanTransaction(LoanTransactionType.REPAYMENT, actualDate.minusDays(1), - OffsetDateTime.of(actualDate, LocalTime.of(11, 0), ZoneOffset.UTC))); - Loan loan = loan(); - given(loan.getLoanTransactions()).willReturn(transactions); - JsonCommand command = jsonCommand(); - // when - GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class, - () -> underTest.validateUndoReAmortize(loan, command)); - // then - assertThat(result).isNotNull(); - assertThat(result.getGlobalisationMessageCode()).isEqualTo("error.msg.loan.reamortize.repayment.exists.after.reamortization"); - } - - @Test - public void testValidateUndoReAmortize_ShouldNotThrowException_WhenLoanAlreadyHasRepaymentAfterReAmortization_SameDay_RepaymentBeforeReAmortization() { + public void testValidateUndoReAmortize_ShouldNotThrowException() { // given List<LoanTransaction> transactions = List.of(loanTransaction(LoanTransactionType.DISBURSEMENT, actualDate.minusDays(2)), loanTransaction(LoanTransactionType.REAMORTIZE, actualDate.minusDays(1), @@ -240,9 +203,8 @@ class LoanReAmortizationValidatorTest { OffsetDateTime.of(actualDate, LocalTime.of(9, 0), ZoneOffset.UTC))); Loan loan = loan(); given(loan.getLoanTransactions()).willReturn(transactions); - JsonCommand command = jsonCommand(); // when - underTest.validateUndoReAmortize(loan, command); + underTest.findAndValidateReAmortizeTransactionForUndo(loan); // then no exception thrown } @@ -271,6 +233,7 @@ class LoanReAmortizationValidatorTest { given(loanTransaction.getTypeOf()).willReturn(type); given(loanTransaction.getTransactionDate()).willReturn(txDate); given(loanTransaction.getSubmittedOnDate()).willReturn(txDate); + given(loanTransaction.isNotReversed()).willReturn(true); return loanTransaction; }
