This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit acabf160039a54bde860ee83b6bd902071de1c25 Author: mariiaKraievska <[email protected]> AuthorDate: Tue Mar 18 19:08:22 2025 +0200 FINERACT-2148: Cumulative Loan - accrual handling in case of charged-off loan --- ...eLoanRepaymentScheduleTransactionProcessor.java | 5 ++ .../test/data/loanproduct/DefaultLoanProduct.java | 1 + .../global/LoanProductGlobalInitializerStep.java | 13 +++ .../fineract/test/support/TestContextKey.java | 1 + .../test/resources/features/LoanChargeOff.feature | 94 ++++++++++++++++++++- .../features/LoanInterestPaymentWaiver.feature | 3 + ...tLoanRepaymentScheduleTransactionProcessor.java | 96 +++++++++++++++++++++- ...eLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...tLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...eLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...tLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...eLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...yLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...rLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...rLoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...ILoanRepaymentScheduleTransactionProcessor.java | 5 ++ ...dvancedPaymentScheduleTransactionProcessor.java | 11 ++- .../starter/LoanAccountAutoStarter.java | 44 ++++++---- ...nRepaymentScheduleTransactionProcessorTest.java | 2 +- ...nRepaymentScheduleTransactionProcessorTest.java | 2 +- .../LoanChargeOffAccountingTest.java | 29 ++++--- 21 files changed, 308 insertions(+), 38 deletions(-) diff --git a/custom/acme/loan/processor/src/main/java/com/acme/fineract/loan/processor/AcmeLoanRepaymentScheduleTransactionProcessor.java b/custom/acme/loan/processor/src/main/java/com/acme/fineract/loan/processor/AcmeLoanRepaymentScheduleTransactionProcessor.java index dc09537447..c8ae5147f6 100644 --- a/custom/acme/loan/processor/src/main/java/com/acme/fineract/loan/processor/AcmeLoanRepaymentScheduleTransactionProcessor.java +++ b/custom/acme/loan/processor/src/main/java/com/acme/fineract/loan/processor/AcmeLoanRepaymentScheduleTransactionProcessor.java @@ -18,6 +18,7 @@ */ package com.acme.fineract.loan.processor; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor; import org.springframework.stereotype.Component; @@ -28,6 +29,10 @@ public class AcmeLoanRepaymentScheduleTransactionProcessor extends FineractStyle public static final String STRATEGY_NAME = "ACME Corp.: standard loan transaction processing strategy"; + public AcmeLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java index 8e6fb34277..2968027252 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java @@ -25,6 +25,7 @@ public enum DefaultLoanProduct implements LoanProduct { LP1_INTEREST_FLAT, // LP1_INTEREST_DECLINING_BALANCE_PERIOD_SAME_AS_PAYMENT, // LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY, // + LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_INT_RECALC, // LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_ACCRUAL_ACTIVITY, // LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_ACCRUAL_ACTIVITY, // LP1_1MONTH_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_MONTHLY, // diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java index 52a9bf34ef..89ab6432eb 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java @@ -1948,6 +1948,19 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ TestContext.INSTANCE.set( TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_LEAP_YEAR_INTEREST_RECALCULATION_DAILY, responseLoanProductsRequestLP2AdvancedPaymentInterestEmiActualActualLeapYearInterestRecalculationDaily); + + // LP1 with 12% DECLINING BALANCE interest, interest period: Daily, interest recalculation enabled + // (LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_INT_RECALC) + final String name81 = DefaultLoanProduct.LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_INT_RECALC.getName(); + final PostLoanProductsRequest loanProductsRequestInterestDecliningPeriodDailyIntRecalc = loanProductsRequestFactory + .defaultLoanProductsRequestLP1InterestDeclining().name(name81).isInterestRecalculationEnabled(true) + .preClosureInterestCalculationStrategy(1).rescheduleStrategyMethod(1).interestRecalculationCompoundingMethod(0) + .recalculationRestFrequencyType(2).recalculationRestFrequencyInterval(1) + .interestCalculationPeriodType(InterestCalculationPeriodTime.DAILY.value).allowPartialPeriodInterestCalcualtion(false); + final Response<PostLoanProductsResponse> responseInterestDecliningPeriodDailyIntRecalc = loanProductsApi + .createLoanProduct(loanProductsRequestInterestDecliningPeriodDailyIntRecalc).execute(); + TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_DAILY_INT_RECALC, + responseInterestDecliningPeriodDailyIntRecalc); } public static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule, 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 1109bbe1fc..0ad79a2822 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 @@ -68,6 +68,7 @@ public abstract class TestContextKey { public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_RESCHEDULE_REDUCE_NR_INSTALLMENTS = "loanProductCreateResponseLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneRescheduleReduceNrInstallments"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_RESCHEDULE_NEXT_REPAYMENTS = "loanProductCreateResponseLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneRescheduleNextRepayments"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_DAILY = "loanProductCreateResponseLP1InterestDecliningPeriodDaily"; + public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_DAILY_INT_RECALC = "loanProductCreateResponseLP1InterestDecliningPeriodDailyIntRecalc"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_DAILY_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP1InterestDecliningPeriodDailyAccrualActivity"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP1InterestDecliningBalanceRecalculationCompoundingNoneAccrualActivity"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION = "loanProductCreateResponseLP2DownPaymentAutoAdvancedPaymentAllocation"; diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature index 5e181f0e56..0059dbac85 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature @@ -847,6 +847,7 @@ Feature: Charge-off Then Loan Transactions tab has the following data: | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | + | 22 February 2023 | Accrual | 17.5 | 0.0 | 17.5 | 0.0 | 0.0 | 0.0 | | 22 February 2023 | Charge-off | 1143.0 | 1000.0 | 30.0 | 103.0 | 10.0 | 0.0 | @TestRailId:C2761 @@ -1204,6 +1205,7 @@ Feature: Charge-off | 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | | 02 January 2023 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | | 03 January 2023 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | + | 05 January 2023 | Accrual | 0.65 | 0.0 | 0.65 | 0.0 | 0.0 | 0.0 | | 05 January 2023 | Charge-off | 1010.19 | 1000.0 | 10.19 | 0.0 | 0.0 | 0.0 | @TestRailId:C2780 @@ -1234,6 +1236,7 @@ Feature: Charge-off | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | | 02 January 2023 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | + | 04 January 2023 | Accrual | 0.66 | 0.0 | 0.66 | 0.0 | 0.0 | 0.0 | | 04 January 2023 | Charge-off | 1010.19 | 1000.0 | 10.19 | 0.0 | 0.0 | 0.0 | When Admin sets the business date to "05 January 2023" When Admin runs inline COB job for Loan @@ -1253,8 +1256,9 @@ Feature: Charge-off | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | | 02 January 2023 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | + | 04 January 2023 | Accrual | 0.66 | 0.0 | 0.66 | 0.0 | 0.0 | 0.0 | | 04 January 2023 | Charge-off | 1010.19 | 1000.0 | 10.19 | 0.0 | 0.0 | 0.0 | - | 06 January 2023 | Accrual | 1.31 | 0.0 | 1.31 | 0.0 | 0.0 | 0.0 | + | 06 January 2023 | Accrual | 0.65 | 0.0 | 0.65 | 0.0 | 0.0 | 0.0 | Then On Loan Transactions tab the "Charge-off" Transaction with date "04 January 2023" is reverted @TestRailId:C2781 @@ -1285,6 +1289,7 @@ Feature: Charge-off | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | | 02 January 2023 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | + | 04 January 2023 | Accrual | 0.66 | 0.0 | 0.66 | 0.0 | 0.0 | 0.0 | | 04 January 2023 | Charge-off | 1010.19 | 1000.0 | 10.19 | 0.0 | 0.0 | 0.0 | When Admin sets the business date to "05 January 2023" When Admin runs inline COB job for Loan @@ -1302,9 +1307,96 @@ Feature: Charge-off | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | | 02 January 2023 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | + | 04 January 2023 | Accrual | 0.66 | 0.0 | 0.66 | 0.0 | 0.0 | 0.0 | | 04 January 2023 | Charge-off | 1010.19 | 1000.0 | 10.19 | 0.0 | 0.0 | 0.0 | | 06 January 2023 | Repayment | 1010.19 | 1000.0 | 10.19 | 0.0 | 0.0 | 0.0 | + Scenario: Accrual handling in case of charged-off loan when charge-off behavior is regular, interestRecalculation = true, cumulative loan + When Admin sets the business date to "1 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 | + | LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_INT_RECALC | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER | + And Admin successfully approves the loan on "1 January 2024" with "100" amount and expected disbursement date on "1 January 2024" + And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount + When Admin runs inline COB job for Loan + 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.59 | 16.41 | 0.59 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.54 | 0.46 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 3 | 31 | 01 April 2024 | | 50.45 | 16.6 | 0.4 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 4 | 30 | 01 May 2024 | | 33.74 | 16.71 | 0.29 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 5 | 31 | 01 June 2024 | | 16.94 | 16.8 | 0.2 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.94 | 0.1 | 0.0 | 0.0 | 17.04 | 0.0 | 0.0 | 0.0 | 17.04 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100 | 2.04 | 0 | 0 | 102.04 | 0 | 0 | 0 | 102.04 | + When Admin sets the business date to "14 February 2024" + When Admin runs inline COB job for Loan + And Admin does charge-off the loan on "14 February 2024" + 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.59 | 16.41 | 0.59 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 2 | 29 | 01 March 2024 | | 67.1 | 16.49 | 0.51 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 3 | 31 | 01 April 2024 | | 50.5 | 16.6 | 0.4 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 4 | 30 | 01 May 2024 | | 33.79 | 16.71 | 0.29 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 5 | 31 | 01 June 2024 | | 16.99 | 16.8 | 0.2 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.99 | 0.1 | 0.0 | 0.0 | 17.09 | 0.0 | 0.0 | 0.0 | 17.09 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100 | 2.09 | 0 | 0 | 102.09 | 0 | 0 | 0 | 102.09 | + 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 | + | 02 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 03 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 04 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 05 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 06 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 07 January 2024 | Accrual | 0.01 | 0.0 | 0.01 | 0.0 | 0.0 | 0.0 | false | false | + | 08 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 09 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 10 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 11 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 12 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 13 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 14 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 15 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 16 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 17 January 2024 | Accrual | 0.01 | 0.0 | 0.01 | 0.0 | 0.0 | 0.0 | false | false | + | 18 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 19 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 20 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 21 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 22 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 23 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 24 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 25 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 26 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 27 January 2024 | Accrual | 0.01 | 0.0 | 0.01 | 0.0 | 0.0 | 0.0 | false | false | + | 28 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 29 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 30 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 31 January 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 01 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 02 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 03 February 2024 | Accrual | 0.01 | 0.0 | 0.01 | 0.0 | 0.0 | 0.0 | false | false | + | 04 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 05 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 06 February 2024 | Accrual | 0.01 | 0.0 | 0.01 | 0.0 | 0.0 | 0.0 | false | false | + | 07 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 08 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 09 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 10 February 2024 | Accrual | 0.01 | 0.0 | 0.01 | 0.0 | 0.0 | 0.0 | false | false | + | 11 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 12 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 13 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 14 February 2024 | Accrual | 0.02 | 0.0 | 0.02 | 0.0 | 0.0 | 0.0 | false | false | + | 14 February 2024 | Charge-off | 102.09 | 100.0 | 2.09 | 0.0 | 0.0 | 0.0 | false | false | + Then LoanAccrualTransactionCreatedBusinessEvent is raised on "14 February 2024" + @TestRailId:C2782 Scenario: Verify that the accrual of charges is not happened when the loan is charged-off on charge's due date but resumed when the charge-off is reverted When Admin sets the business date to "01 January 2023" diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestPaymentWaiver.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestPaymentWaiver.feature index ea6e7ba54b..995a7310e7 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestPaymentWaiver.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestPaymentWaiver.feature @@ -606,6 +606,7 @@ Feature: LoanInterestWaiver | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | | 01 February 2024 | Interest Payment Waiver | 260.0 | 250.0 | 10.0 | 0.0 | 0.0 | 750.0 | + | 02 February 2024 | Accrual | 10.34 | 0.0 | 10.34 | 0.0 | 0.0 | 0.0 | | 02 February 2024 | Charge-off | 780.0 | 750.0 | 30.0 | 0.0 | 0.0 | 0.0 | Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | @@ -647,6 +648,7 @@ Feature: LoanInterestWaiver Then Loan Transactions tab has the following data: | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | + | 15 January 2024 | Accrual | 4.52 | 0.0 | 4.52 | 0.0 | 0.0 | 0.0 | | 15 January 2024 | Charge-off | 1040.0 | 1000.0 | 40.0 | 0.0 | 0.0 | 0.0 | Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | @@ -673,6 +675,7 @@ Feature: LoanInterestWaiver Then Loan Transactions tab has the following data: | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | | 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | + | 15 January 2024 | Accrual | 4.52 | 0.0 | 4.52 | 0.0 | 0.0 | 0.0 | | 15 January 2024 | Charge-off | 1040.0 | 1000.0 | 40.0 | 0.0 | 0.0 | 0.0 | | 01 February 2024 | Interest Payment Waiver | 260.0 | 250.0 | 10.0 | 0.0 | 0.0 | 750.0 | Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries: 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 765df9f396..e271c13786 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 @@ -18,8 +18,14 @@ */ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor; +import static java.math.BigDecimal.ZERO; +import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction.accrualAdjustment; +import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction.accrueTransaction; + import java.math.BigDecimal; +import java.math.MathContext; import java.time.LocalDate; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -28,15 +34,19 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidDetail; import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData; import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; +import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour; import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy; import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; @@ -62,10 +72,12 @@ import org.springframework.util.CollectionUtils; * @see HeavensFamilyLoanRepaymentScheduleTransactionProcessor * @see CreocoreLoanRepaymentScheduleTransactionProcessor */ +@RequiredArgsConstructor public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implements LoanRepaymentScheduleTransactionProcessor { - public final SingleLoanChargeRepaymentScheduleProcessingWrapper loanChargeProcessor = new SingleLoanChargeRepaymentScheduleProcessingWrapper(); - public final LoanChargeValidator loanChargeValidator = new LoanChargeValidator(); + protected final SingleLoanChargeRepaymentScheduleProcessingWrapper loanChargeProcessor = new SingleLoanChargeRepaymentScheduleProcessingWrapper(); + protected final LoanChargeValidator loanChargeValidator = new LoanChargeValidator(); + protected final ExternalIdFactory externalIdFactory; @Override public boolean accept(String s) { @@ -399,6 +411,12 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction, MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments) { final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction); + + final BigDecimal newInterest = getInterestTillChargeOffForPeriod(newLoanTransaction.getLoan(), + newLoanTransaction.getTransactionDate()); + createMissingAccrualTransactionDuringChargeOffIfNeeded(newInterest, newLoanTransaction, newLoanTransaction.getTransactionDate(), + changedTransactionDetail); + newLoanTransaction.resetDerivedComponents(); // determine how much is outstanding total and breakdown for principal, interest and charges Money principalPortion = Money.zero(currency); @@ -933,4 +951,78 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen } return latestCharge; } + + private BigDecimal getInterestTillChargeOffForPeriod(final Loan loan, final LocalDate chargeOffDate) { + BigDecimal interestTillChargeOff = BigDecimal.ZERO; + final MonetaryCurrency currency = loan.getCurrency(); + + final List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments().stream() + .filter(i -> !i.isAdditional()).toList(); + + for (LoanRepaymentScheduleInstallment installment : installments) { + final boolean isPastPeriod = !installment.getDueDate().isAfter(chargeOffDate); + final boolean isInPeriod = !installment.getFromDate().isAfter(chargeOffDate) && installment.getDueDate().isAfter(chargeOffDate); + + BigDecimal interest = BigDecimal.ZERO; + + if (isPastPeriod) { + interest = installment.getInterestCharged(currency).minus(installment.getCreditedInterest()).getAmount(); + } else if (isInPeriod) { + final BigDecimal totalInterest = installment.getInterestOutstanding(currency).getAmount(); + if (LoanChargeOffBehaviour.ZERO_INTEREST.equals(loan.getLoanProductRelatedDetail().getChargeOffBehaviour()) + || LoanChargeOffBehaviour.ACCELERATE_MATURITY.equals(loan.getLoanProductRelatedDetail().getChargeOffBehaviour())) { + interest = totalInterest; + } else { + final long totalDaysInPeriod = ChronoUnit.DAYS.between(installment.getFromDate(), installment.getDueDate()); + final long daysTillChargeOff = ChronoUnit.DAYS.between(installment.getFromDate(), chargeOffDate); + final MathContext mc = MoneyHelper.getMathContext(); + + interest = Money.of(currency, totalInterest.divide(BigDecimal.valueOf(totalDaysInPeriod), mc) + .multiply(BigDecimal.valueOf(daysTillChargeOff), mc), mc).getAmount(); + } + } + interestTillChargeOff = interestTillChargeOff.add(interest); + } + + return interestTillChargeOff; + } + + private void createMissingAccrualTransactionDuringChargeOffIfNeeded(final BigDecimal newInterest, + final LoanTransaction chargeOffTransaction, final LocalDate chargeOffDate, + final ChangedTransactionDetail changedTransactionDetail) { + final Loan loan = chargeOffTransaction.getLoan(); + final List<LoanRepaymentScheduleInstallment> relevantInstallments = loan.getRepaymentScheduleInstallments().stream() + .filter(i -> !i.getFromDate().isAfter(chargeOffDate)).toList(); + + if (relevantInstallments.isEmpty()) { + return; + } + + final BigDecimal sumOfAccrualsTillChargeOff = loan.getLoanTransactions().stream() + .filter(lt -> lt.isAccrual() && !lt.getTransactionDate().isAfter(chargeOffDate) && lt.isNotReversed()) + .map(lt -> Optional.ofNullable(lt.getInterestPortion()).orElse(BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add); + + final BigDecimal sumOfAccrualAdjustmentsTillChargeOff = loan.getLoanTransactions().stream() + .filter(lt -> lt.isAccrualAdjustment() && !lt.getTransactionDate().isAfter(chargeOffDate) && lt.isNotReversed()) + .map(lt -> Optional.ofNullable(lt.getInterestPortion()).orElse(BigDecimal.ZERO)).reduce(BigDecimal.ZERO, BigDecimal::add); + + final BigDecimal missingAccrualAmount = newInterest.subtract(sumOfAccrualsTillChargeOff).add(sumOfAccrualAdjustmentsTillChargeOff); + + if (missingAccrualAmount.compareTo(BigDecimal.ZERO) == 0) { + return; + } + + final LoanTransaction newAccrualTransaction; + + if (missingAccrualAmount.compareTo(BigDecimal.ZERO) > 0) { + newAccrualTransaction = accrueTransaction(loan, loan.getOffice(), chargeOffDate, missingAccrualAmount, missingAccrualAmount, + ZERO, ZERO, externalIdFactory.create()); + } else { + newAccrualTransaction = accrualAdjustment(loan, loan.getOffice(), chargeOffDate, missingAccrualAmount.abs(), + missingAccrualAmount.abs(), ZERO, ZERO, externalIdFactory.create()); + } + + changedTransactionDetail.addNewTransactionChangeBeforeExistingOne(new TransactionChangeData(null, newAccrualTransaction), + chargeOffTransaction); + } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java index 50f1672606..eed45dd84a 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im import java.time.LocalDate; import java.util.List; import java.util.Set; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -46,6 +47,10 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL public static final String STRATEGY_NAME = "Creocore Unique"; + public CreocoreLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java index 95ca1bab1f..fb82ae93bd 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java @@ -22,6 +22,7 @@ import java.time.LocalDate; import java.util.List; import java.util.Set; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -47,6 +48,10 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio public static final String STRATEGY_NAME = "Due penalty, fee, interest, principal, In advance principal, penalty, fee, interest"; + public DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java index b87ced9bd8..acdee6768c 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java @@ -22,6 +22,7 @@ import java.time.LocalDate; import java.util.List; import java.util.Set; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -47,6 +48,10 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio public static final String STRATEGY_NAME = "Due penalty, interest, principal, fee, In advance penalty, interest, principal, fee"; + public DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java index 6d62063aad..8d7794a570 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im import java.time.LocalDate; import java.util.List; import java.util.Set; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -40,6 +41,10 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr public static final String STRATEGY_NAME = "Early Repayment Strategy"; + public EarlyPaymentLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java index 8e563299bc..0b3de1210c 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im import java.time.LocalDate; import java.util.List; import java.util.Set; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -46,6 +47,10 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst public static final String STRATEGY_NAME = "Penalties, Fees, Interest, Principal order"; + public FineractStyleLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java index f9d065abe6..67356bc58c 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java @@ -22,6 +22,7 @@ import java.time.LocalDate; import java.util.List; import java.util.Set; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -48,6 +49,10 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst public static final String STRATEGY_NAME = "HeavensFamily Unique"; + public HeavensFamilyLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java index 849a8478a7..5ed1824f6f 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im import java.time.LocalDate; import java.util.List; import java.util.Set; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -41,6 +42,10 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr public static final String STRATEGY_NAME = "Interest, Principal, Penalties, Fees Order"; + public InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java index ff1cd3e9e3..a134067fb8 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im import java.time.LocalDate; import java.util.List; import java.util.Set; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -41,6 +42,10 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr public static final String STRATEGY_NAME = "Principal, Interest, Penalties, Fees Order"; + public PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java index fbaa5522c4..47ee06451f 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java @@ -22,6 +22,7 @@ import java.time.LocalDate; import java.util.List; import java.util.Set; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; @@ -50,6 +51,10 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe public static final String STRATEGY_NAME = "Overdue/Due Fee/Int,Principal"; + public RBILoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + } + @Override public String getCode() { return STRATEGY_CODE; diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index 6d25be7b39..b61ee7da52 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -54,7 +54,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.NotImplementedException; @@ -107,7 +106,6 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Slf4j -@RequiredArgsConstructor public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor { public static final String ADVANCED_PAYMENT_ALLOCATION_STRATEGY = "advanced-payment-allocation-strategy"; @@ -116,7 +114,14 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep private final EMICalculator emiCalculator; private final LoanRepositoryWrapper loanRepositoryWrapper; private final InterestRefundService interestRefundService; - private final ExternalIdFactory externalIdFactory; + + public AdvancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, LoanRepositoryWrapper loanRepositoryWrapper, + InterestRefundService interestRefundService, ExternalIdFactory externalIdFactory) { + super(externalIdFactory); + this.emiCalculator = emiCalculator; + this.loanRepositoryWrapper = loanRepositoryWrapper; + this.interestRefundService = interestRefundService; + } @Override public String getCode() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java index 8737fb81d8..4a88a47def 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java @@ -46,56 +46,64 @@ public class LoanAccountAutoStarter { @Bean @Conditional(CreocoreLoanRepaymentScheduleTransactionProcessorCondition.class) - public CreocoreLoanRepaymentScheduleTransactionProcessor creocoreLoanRepaymentScheduleTransactionProcessor() { - return new CreocoreLoanRepaymentScheduleTransactionProcessor(); + public CreocoreLoanRepaymentScheduleTransactionProcessor creocoreLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new CreocoreLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(EarlyRepaymentLoanRepaymentScheduleTransactionProcessorCondition.class) - public EarlyPaymentLoanRepaymentScheduleTransactionProcessor earlyPaymentLoanRepaymentScheduleTransactionProcessor() { - return new EarlyPaymentLoanRepaymentScheduleTransactionProcessor(); + public EarlyPaymentLoanRepaymentScheduleTransactionProcessor earlyPaymentLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new EarlyPaymentLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(MifosStandardLoanRepaymentScheduleTransactionProcessorCondition.class) - public FineractStyleLoanRepaymentScheduleTransactionProcessor fineractStyleLoanRepaymentScheduleTransactionProcessor() { - return new FineractStyleLoanRepaymentScheduleTransactionProcessor(); + public FineractStyleLoanRepaymentScheduleTransactionProcessor fineractStyleLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new FineractStyleLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(HeavensFamilyLoanRepaymentScheduleTransactionProcessorCondition.class) - public HeavensFamilyLoanRepaymentScheduleTransactionProcessor heavensFamilyLoanRepaymentScheduleTransactionProcessor() { - return new HeavensFamilyLoanRepaymentScheduleTransactionProcessor(); + public HeavensFamilyLoanRepaymentScheduleTransactionProcessor heavensFamilyLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new HeavensFamilyLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(InterestPrincipalPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class) - public InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor interestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() { - return new InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor(); + public InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor interestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(PrincipalInterestPenaltiesFeesLoanRepaymentScheduleTransactionProcessorCondition.class) - public PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor principalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor() { - return new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor(); + public PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor principalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(RBIIndiaLoanRepaymentScheduleTransactionProcessorCondition.class) - public RBILoanRepaymentScheduleTransactionProcessor rbiLoanRepaymentScheduleTransactionProcessor() { - return new RBILoanRepaymentScheduleTransactionProcessor(); + public RBILoanRepaymentScheduleTransactionProcessor rbiLoanRepaymentScheduleTransactionProcessor(ExternalIdFactory externalIdFactory) { + return new RBILoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessorCondition.class) - public DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor duePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor() { - return new DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor(); + public DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor duePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean @Conditional(DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessorCondition.class) - public DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor duePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor() { - return new DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor(); + public DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor duePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor( + ExternalIdFactory externalIdFactory) { + return new DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor(externalIdFactory); } @Bean diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessorTest.java index 38f5369efc..474db7b09a 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessorTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessorTest.java @@ -99,7 +99,7 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio @BeforeEach public void setUp() { - underTest = new DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor(); + underTest = new DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor(null); Mockito.reset(charges, transactionMappings); ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessorTest.java index 6567ae6c5a..94f2d609e8 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessorTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessorTest.java @@ -100,7 +100,7 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio @BeforeEach public void setUp() { - underTest = new DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor(); + underTest = new DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor(null); Mockito.reset(charges, transactionMappings); ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java index 6def38ceac..45da532fe0 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java @@ -780,8 +780,9 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest { loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement()); assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual()); - assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff()); - assertEquals(3, loanDetails.getTransactions().size()); + assertTrue(loanDetails.getTransactions().get(2).getType().getAccrual()); + assertTrue(loanDetails.getTransactions().get(3).getType().getChargeoff()); + assertEquals(4, loanDetails.getTransactions().size()); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 8)); inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue())); @@ -789,8 +790,9 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest { loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement()); assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual()); - assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff()); - assertEquals(3, loanDetails.getTransactions().size()); + assertTrue(loanDetails.getTransactions().get(2).getType().getAccrual()); + assertTrue(loanDetails.getTransactions().get(3).getType().getChargeoff()); + assertEquals(4, loanDetails.getTransactions().size()); loanTransactionHelper.undoChargeOffLoan((long) loanId, new PostLoansLoanIdTransactionsRequest()); // generate accrual again @@ -799,9 +801,10 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest { loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement()); assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual()); - assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff()); - assertTrue(loanDetails.getTransactions().get(3).getType().getAccrual()); - assertEquals(4, loanDetails.getTransactions().size()); + assertTrue(loanDetails.getTransactions().get(2).getType().getAccrual()); + assertTrue(loanDetails.getTransactions().get(3).getType().getChargeoff()); + assertTrue(loanDetails.getTransactions().get(4).getType().getAccrual()); + assertEquals(5, loanDetails.getTransactions().size()); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 10)); @@ -815,11 +818,13 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest { loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement()); assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual()); - assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff()); - assertTrue(loanDetails.getTransactions().get(3).getType().getAccrual()); - assertTrue(loanDetails.getTransactions().get(4).getType().getChargeoff()); - assertTrue(loanDetails.getTransactions().get(5).getType().getRepayment()); - assertEquals(6, loanDetails.getTransactions().size()); + assertTrue(loanDetails.getTransactions().get(2).getType().getAccrual()); + assertTrue(loanDetails.getTransactions().get(3).getType().getChargeoff()); + assertTrue(loanDetails.getTransactions().get(4).getType().getAccrual()); + assertTrue(loanDetails.getTransactions().get(5).getType().getAccrual()); + assertTrue(loanDetails.getTransactions().get(6).getType().getChargeoff()); + assertTrue(loanDetails.getTransactions().get(7).getType().getRepayment()); + assertEquals(8, loanDetails.getTransactions().size()); } finally { globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE, new PutGlobalConfigurationsRequest().enabled(false));
