From 6051477144816acfe292982f082af2370a71a866 Mon Sep 17 00:00:00 2001 From: Rustam Zeinalov Date: Wed, 12 Feb 2025 19:57:15 +0100 Subject: [PATCH] FINERACT-2081: Fix Accrual activity - reverse and replayed couple of times after backdated repayment --- .gitignore | 3 + .../fineract/test/support/TestContextKey.java | 2 +- .../features/LoanAccrualActivity.feature | 12 +-- .../LoanRepaymentScheduleInstallment.java | 5 + .../loanaccount/domain/LoanTransaction.java | 4 + ...RepaymentScheduleTransactionProcessor.java | 12 ++- ...TransactionAccrualActivityPostingTest.java | 95 +++++++++++++++---- 7 files changed, 106 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 5a6fc20cfeb..97b125798f6 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ fineract-provider/src/main/generated/ **/out/ gradleExp/ + +.run/ +.java-version \ No newline at end of file diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index 96bbae8ec2e..de56fc8ab00 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -120,8 +120,8 @@ public abstract class TestContextKey { public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_RECALCULATION_DAILY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRecalculationDaily"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationAccelerateMaturityChargeOffBehaviour"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030AccrualActivity"; - public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentAccelerateMaturityChargeOffBehaviour"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_RECOGNITION_DISBURSEMENT_DAILY_EMI_360_30_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestRecognitionDisbursementDailyEmi36030AccrualActivity"; + public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentAccelerateMaturityChargeOffBehaviour"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_RECOGNITION_DISBURSEMENT_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestRecognitionDisbursementDailyEmiActualActualAccrualActivity"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_EMI_360_30_CHARGEBACK_INTEREST_PENALTY_FEE_PRINCIPAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalcEmi36030ChargebackInterestPenaltyFeePrincipal"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_EMI_360_30_CHARGEBACK_INTEREST_FEE_PRINCIPAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalcEmi36030ChargebackInterestFeePrincipal"; diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature index 2e8f17d92ad..ddd8056f6f3 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature @@ -4584,7 +4584,7 @@ Feature: LoanAccrualActivity | 1 | 29 | 29 February 2024 | | 1671.5 | 328.5 | 11.67 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 2 | 31 | 31 March 2024 | | 1341.14 | 330.36 | 9.81 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 3 | 30 | 30 April 2024 | | 1008.79 | 332.35 | 7.82 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | - | 4 | 31 | 31 May 2024 | | 674.5 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | + | 4 | 31 | 31 May 2024 | | 674.5 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 5 | 30 | 30 June 2024 | | 338.26 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 6 | 31 | 31 July 2024 | | 0.0 | 338.26 | 1.97 | 0.0 | 0.0 | 340.23 | 0.0 | 0.0 | 0.0 | 340.23 | Then Loan Repayment schedule has the following data in Total row: @@ -4629,11 +4629,11 @@ Feature: LoanAccrualActivity | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | | | 31 January 2024 | | 2000.0 | | | 0.0 | | 0.0 | 0.0 | | | | | 1 | 29 | 29 February 2024 | 15 February 2024 | 1665.86 | 334.14 | 6.03 | 0.0 | 0.0 | 340.17 | 340.17 | 340.17 | 0.0 | 0.0 | - | 2 | 31 | 31 March 2024 | | 1340.11 | 325.75 | 14.42 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | + | 2 | 31 | 31 March 2024 | | 1340.11 | 325.75 | 14.42 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 3 | 30 | 30 April 2024 | | 1007.76 | 332.35 | 7.82 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | - | 4 | 31 | 31 May 2024 | | 673.47 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | + | 4 | 31 | 31 May 2024 | | 673.47 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 5 | 30 | 30 June 2024 | | 337.23 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | - | 6 | 31 | 31 July 2024 | | 0.0 | 337.23 | 1.97 | 0.0 | 0.0 | 339.2 | 0.0 | 0.0 | 0.0 | 339.2 | + | 6 | 31 | 31 July 2024 | | 0.0 | 337.23 | 1.97 | 0.0 | 0.0 | 339.2 | 0.0 | 0.0 | 0.0 | 339.2 | Then Loan Repayment schedule has the following data in Total row: | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | 2000.0 | 40.05 | 0.0 | 0.0 | 2040.05 | 340.17 | 340.17 | 0.0 | 1699.88 | @@ -4680,7 +4680,7 @@ Feature: LoanAccrualActivity | 1 | 29 | 29 February 2024 | 15 February 2024 | 1665.86 | 334.14 | 6.03 | 0.0 | 0.0 | 340.17 | 340.17 | 340.17 | 0.0 | 0.0 | | 2 | 31 | 31 March 2024 | | 1340.11 | 325.75 | 14.42 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 3 | 30 | 30 April 2024 | | 1007.82 | 332.29 | 7.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | - | 4 | 31 | 31 May 2024 | | 673.53 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | + | 4 | 31 | 31 May 2024 | | 673.53 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 5 | 30 | 30 June 2024 | | 337.29 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 | | 6 | 31 | 31 July 2024 | | 0.0 | 337.29 | 1.97 | 0.0 | 0.0 | 339.26 | 0.0 | 0.0 | 0.0 | 339.26 | Then Loan Repayment schedule has the following data in Total row: @@ -4721,7 +4721,7 @@ Feature: LoanAccrualActivity | 28 February 2024 | Accrual | 0.41 | 0.0 | 0.41 | 0.0 | 0.0 | 0.0 | false | false | | 29 February 2024 | Accrual | 0.38 | 0.0 | 0.38 | 0.0 | 0.0 | 0.0 | false | false | | 29 February 2024 | Accrual Activity | 6.03 | 0.0 | 6.03 | 0.0 | 0.0 | 0.0 | false | true | - | 01 March 2024 | Accrual Adjustment | 0.69 | 0.0 | 0.69 | 0.0 | 0.0 | 0.0 | false | false | + | 01 March 2024 | Accrual Adjustment | 0.69 | 0.0 | 0.69 | 0.0 | 0.0 | 0.0 | false | false | | 02 March 2024 | Accrual | 0.31 | 0.0 | 0.31 | 0.0 | 0.0 | 0.0 | false | false | | 03 March 2024 | Accrual | 0.31 | 0.0 | 0.31 | 0.0 | 0.0 | 0.0 | false | false | | 04 March 2024 | Accrual | 0.32 | 0.0 | 0.32 | 0.0 | 0.0 | 0.0 | false | false | diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index 87641b54237..78a233a2d6b 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -1058,4 +1058,9 @@ public void resetPrincipalDue() { public enum PaymentAction { PAY, UNPAY } + + public boolean isTransactionDateWithinPeriod(LocalDate transactionDate) { + return !DateUtils.isBefore(transactionDate, getFromDate()) && DateUtils.isBefore(transactionDate, getDueDate()); + } + } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java index a9f7270bcf8..b8de8787586 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java @@ -1035,4 +1035,8 @@ public void updateAmount(BigDecimal bigDecimal) { // TODO missing hashCode(), equals(Object obj), but probably OK as long as // this is never stored in a Collection. + + public void updateTransactionDate(final LocalDate transactionDate) { + this.dateOf = transactionDate; + } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index c6c0da18699..a62f5f5f875 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -239,7 +239,17 @@ protected void calculateAccrualActivity(LoanTransaction loanTransaction, Monetar Money interestPortion = currentInstallment.getInterestCharged(currency); Money feeChargesPortion = currentInstallment.getFeeChargesCharged(currency); Money penaltyChargesPortion = currentInstallment.getPenaltyChargesCharged(currency); - loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + if (interestPortion.plus(feeChargesPortion).plus(penaltyChargesPortion).isZero()) { + loanTransaction.reverse(); + } else { + loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + final Loan loan = loanTransaction.getLoan(); + if ((loan.isClosedObligationsMet() || loan.isOverPaid()) && currentInstallment.isObligationsMet() + && currentInstallment.getObligationsMetOnDate().isBefore(currentInstallment.getDueDate()) + && currentInstallment.isTransactionDateWithinPeriod(loanTransaction.getDateOf())) { + loanTransaction.updateTransactionDate(currentInstallment.getObligationsMetOnDate()); + } + } } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java index c8d754f9da8..b042ec8ff5a 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java @@ -417,7 +417,6 @@ public void testInterestBearingProgressiveNoInterestRecalculationReopenDueRevers transaction(6.48, "Accrual Activity", "17 November 2024"), // transaction(4.75, "Accrual Activity", "17 December 2024"), // transaction(18.31, "Accrual Activity", "17 January 2025")); // - }); } @@ -719,7 +718,7 @@ public void testAccrualActivityPostingReverseReplayAdvancedPaymentAllocation(fin Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(); loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000), - disbursementDay)); + disbursementDay, BigDecimal.ZERO)); loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)) .dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en")); @@ -798,7 +797,7 @@ public void testAccrualActivityPostingReverseReplayAdvancedPaymentAllocationBasi Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(); loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000), - disbursementDay)); + disbursementDay, BigDecimal.ZERO)); loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)) .dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en")); @@ -850,7 +849,7 @@ public void testAccrualActivityPostingForProgressiveLoanWithEarlyRepaymentAndRev runAt(creationBusinessDay, () -> { Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(); loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000), - disbursementDay)); + disbursementDay, BigDecimal.ZERO)); loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)) .dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en")); @@ -955,7 +954,7 @@ public void testAccrualActivityPostingForProgressiveMultiDisburseLoanWithEarlyRe Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true); loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000), - disbursementDay, disbursementDay2)); + disbursementDay, disbursementDay2, BigDecimal.ZERO)); loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)) .dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en")); @@ -1014,7 +1013,7 @@ public void testAccrualActivityPostingForProgressiveMultiDisburseLoanWithEarlyRe Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true); loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000), - disbursementDay, disbursementDay2)); + disbursementDay, disbursementDay2, BigDecimal.ZERO)); loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)) .dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en")); @@ -1119,7 +1118,7 @@ public void testAccrualActivityPostingForMultiDisburseProgressiveLoan() { runAt(creationBusinessDay, () -> { Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true); loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), - disbursementDay, disbursementDay2)); + disbursementDay, disbursementDay2, BigDecimal.ZERO)); loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)) .dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en")); @@ -1391,6 +1390,41 @@ public void test() { }); } + @Test + public void testReverseAndReplayedCoupleOfTimesAfterBackdatedRepayment() { + final String disbursementDay = "01 January 2025"; + AtomicReference loanId = new AtomicReference<>(); + runAt(disbursementDay, () -> { + Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocationInterestRecalculation(true); + loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(800), + disbursementDay, disbursementDay, BigDecimal.valueOf(0.3))); + + loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(800)) + .dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en")); + + loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay) + .dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(800.0)).locale("en")); + }); + + runAt("02 February 2025", () -> { + inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get())); + verifyTransactions(loanId.get(), + transaction(10.60, "Accrual Activity", "01 February 2025", 0.0, 0.0, 10.60, 0.0, 0.0, 0.0, 0.0, false), // + transaction(10.60, "Accrual", "01 February 2025", 0.0, 0.0, 10.60, 0.0, 0.0, 0.0, 0.0, false), // + transaction(800.0, "Disbursement", disbursementDay, 800.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); + + loanTransactionHelper.makeLoanRepayment("31 January 2025", 900.0F, loanId.get().intValue()); + + verifyTransactions(loanId.get(), + transaction(0.34, "Accrual Adjustment", "02 February 2025", 0.0, 0.0, 0.34, 0.0, 0.0, 0.0, 0.0, false), // + transaction(10.60, "Accrual", "01 February 2025", 0.0, 0.0, 10.60, 0.0, 0.0, 0.0, 0.0, false), // + transaction(10.26, "Accrual Activity", "31 January 2025", 0.0, 0.0, 10.26, 0.0, 0.0, 0.0, 0.0, false), // + transaction(900.0, "Repayment", "31 January 2025", 0.0, 800, 10.26, 0.0, 0.0, 0.0, 89.74, false), // + transaction(800.0, "Disbursement", disbursementDay, 800.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); + + }); + } + private PostLoanProductsRequest loanProductsRequestInterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity() { String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6); String shortName = Utils.uniqueRandomStringGenerator("", 4); @@ -1439,6 +1473,7 @@ private PostLoanProductsRequest loanProductsRequestInterestDecliningBalanceDaily .minInterestRatePerPeriod(0.0)// .interestRatePerPeriod(12.0)// .maxInterestRatePerPeriod(30.0)// + .interestRateFrequencyType(2)// Month .interestRateFrequencyType(3)// .repaymentEvery(30)// .repaymentFrequencyType(0L)// @@ -1631,19 +1666,28 @@ private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation } private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(boolean isMultiDisburse) { + Long resourceId = loanTransactionHelper + .createLoanProduct(loanProductAccountingAccrualAdvanvedPaymentAllocationAccrualActivity(isMultiDisburse)).getResourceId(); + LOG.info("Test Progressive Loan Product Id {} isMultiDisburse {} http://localhost:4200/#/products/loan-products/{1}/general", + resourceId, isMultiDisburse); + return resourceId; + } + + private PostLoanProductsRequest loanProductAccountingAccrualAdvanvedPaymentAllocationAccrualActivity(boolean isMultiDisburse) { String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6); String shortName = Utils.uniqueRandomStringGenerator("", 4); AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation(); - Long resourceId = loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest().name(name).shortName(shortName) - .multiDisburseLoan(isMultiDisburse).maxTrancheCount(isMultiDisburse ? 2 : 1).interestType(isMultiDisburse ? 0 : 1) + return new PostLoanProductsRequest().name(name).shortName(shortName).multiDisburseLoan(isMultiDisburse) + .maxTrancheCount(isMultiDisburse ? 2 : 1).interestType(isMultiDisburse ? 0 : 1) .interestCalculationPeriodType(isMultiDisburse ? 0 : 1).disallowExpectedDisbursements(isMultiDisburse) .description("Test loan description").currencyCode("USD").digitsAfterDecimal(2).daysInYearType(1).daysInMonthType(1) .recalculationRestFrequencyType(1).rescheduleStrategyMethod(1).loanScheduleType(LoanScheduleType.PROGRESSIVE.name()) - .recalculationRestFrequencyInterval(0).isInterestRecalculationEnabled(false).locale("en_GB").numberOfRepayments(4) - .repaymentFrequencyType(2L).repaymentEvery(1).minPrincipal(100.0).principal(1000.0).maxPrincipal(10000000.0) - .amortizationType(1).interestRatePerPeriod(0.0).interestRateFrequencyType(1).dateFormat("dd MMMM yyyy") + .recalculationRestFrequencyInterval(0).locale("en_GB").numberOfRepayments(4).repaymentFrequencyType(2L).repaymentEvery(1) + .minPrincipal(100.0).principal(1000.0).maxPrincipal(10000000.0).amortizationType(1).interestRatePerPeriod(0.0) + .interestRateFrequencyType(1).dateFormat("dd MMMM yyyy") .transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).paymentAllocation(List.of(defaultAllocation)) - .accountingRule(3).enableAccrualActivityPosting(true).fundSourceAccountId(fundSource.getAccountID().longValue())// + .accountingRule(3).isInterestRecalculationEnabled(false).enableAccrualActivityPosting(true) + .fundSourceAccountId(fundSource.getAccountID().longValue())// .loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())// .transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())// .interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())// @@ -1663,18 +1707,30 @@ private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation .incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())// .chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())// .chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())// - .incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())// - ).getResourceId(); + .incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue());// + } + + private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocationInterestRecalculation(boolean isMultiDisburse) { + Long resourceId = loanTransactionHelper + .createLoanProduct(loanProductAccountingAccrualAdvanvedPaymentAllocationAccrualActivity(isMultiDisburse) // + .interestRatePerPeriod(10.0) // + .isInterestRecalculationEnabled(true)// + .preClosureInterestCalculationStrategy(1) // TILL_PRE_CLOSE_DATE + .rescheduleStrategyMethod(4) // ADJUST_LAST_UNPAID_PERIOD + .interestRecalculationCompoundingMethod(0) // NONE + .recalculationRestFrequencyType(2) // DAILY + .recalculationRestFrequencyInterval(1)// + ).getResourceId(); LOG.info("Test Progressive Loan Product Id {} isMultiDisburse {} http://localhost:4200/#/products/loan-products/{1}/general", resourceId, isMultiDisburse); return resourceId; } private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID, - BigDecimal principal, String applicationDisbursementDate) { + BigDecimal principal, String applicationDisbursementDate, BigDecimal interestRatePerPeriod) { final PostLoansRequest loanRequest = new PostLoansRequest() // .loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2) - .repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(BigDecimal.ZERO) + .repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(interestRatePerPeriod) .interestCalculationPeriodType(1).dateFormat("dd MMMM yyyy") .transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual") .expectedDisbursementDate(applicationDisbursementDate).submittedOnDate(applicationDisbursementDate).clientId(clientID) @@ -1686,10 +1742,11 @@ private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long } private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID, - BigDecimal principal, String applicationDisbursementDate, String applicationDisbursementDate2) { + BigDecimal principal, String applicationDisbursementDate, String applicationDisbursementDate2, + BigDecimal interestRatePerPeriod) { final PostLoansRequest loanRequest = new PostLoansRequest() // .loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2) - .repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(BigDecimal.ZERO) + .repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(interestRatePerPeriod) .interestCalculationPeriodType(0).dateFormat("dd MMMM yyyy") .transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual") .submittedOnDate(applicationDisbursementDate).clientId(clientID).expectedDisbursementDate(applicationDisbursementDate2)