From a7ad33abff83ed8196d72c7d0d181afa8feee00e Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Tue, 9 Apr 2024 13:00:29 +0200 Subject: [PATCH] FINERACT-2042: Fix chargeback on overpaid loan --- .../resources/features/LoanChargeback.feature | 140 +++++++++++++++++- .../portfolio/loanaccount/domain/Loan.java | 38 +++-- ...edPaymentScheduleTransactionProcessor.java | 1 + .../service/LoanReadPlatformServiceImpl.java | 11 +- .../reaging/LoanReAgingServiceImpl.java | 4 +- .../LoanReAmortizationServiceImpl.java | 13 +- 6 files changed, 167 insertions(+), 40 deletions(-) diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature index 8448fa1f8..140dc069a 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature @@ -2160,8 +2160,8 @@ Feature: LoanChargeback | | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | | 1 | 0 | 01 January 2024 | 04 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 125.0 | 0.0 | | 2 | 15 | 16 January 2024 | 16 January 2024 | 250.0 | 125.0 | 0.0 | 0.0 | 3.0 | 128.0 | 128.0 | 0.0 | 0.0 | 0.0 | - | 3 | 15 | 31 January 2024 | 31 January 2024 | 128.0 | 250.0 | 0.0 | 0.0 | 3.0 | 253.0 | 253.0 | 0.0 | 0.0 | 0.0 | - | 4 | 15 | 15 February 2024 | 15 February 2024 | 3.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | + | 3 | 15 | 31 January 2024 | 31 January 2024 | 125.0 | 250.0 | 0.0 | 0.0 | 3.0 | 253.0 | 253.0 | 0.0 | 0.0 | 0.0 | + | 4 | 15 | 15 February 2024 | 15 February 2024 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | Then Loan Repayment schedule has the following data in Total row: | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | 625.0 | 0.0 | 0.0 | 6.0 | 631.0 | 631.0 | 0.0 | 125.0 | 0.0 | @@ -2200,8 +2200,8 @@ Feature: LoanChargeback | | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | | 1 | 0 | 01 January 2024 | 04 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 125.0 | 0.0 | | 2 | 15 | 16 January 2024 | 16 January 2024 | 250.0 | 125.0 | 0.0 | 0.0 | 3.0 | 128.0 | 128.0 | 0.0 | 0.0 | 0.0 | - | 3 | 15 | 31 January 2024 | 15 February 2024 | 128.0 | 175.0 | 0.0 | 0.0 | 3.0 | 178.0 | 178.0 | 0.0 | 3.0 | 0.0 | - | 4 | 15 | 15 February 2024 | 15 February 2024 | 3.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | + | 3 | 15 | 31 January 2024 | 15 February 2024 | 125.0 | 175.0 | 0.0 | 0.0 | 3.0 | 178.0 | 178.0 | 0.0 | 3.0 | 0.0 | + | 4 | 15 | 15 February 2024 | 15 February 2024 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | Then Loan Repayment schedule has the following data in Total row: | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | 550.0 | 0.0 | 0.0 | 6.0 | 556.0 | 556.0 | 0.0 | 128.0 | 0.0 | @@ -2242,8 +2242,8 @@ Feature: LoanChargeback | | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | | 1 | 0 | 01 January 2024 | 04 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 125.0 | 0.0 | | 2 | 15 | 16 January 2024 | 31 January 2024 | 250.0 | 125.0 | 0.0 | 4.0 | 3.0 | 132.0 | 132.0 | 0.0 | 7.0 | 0.0 | - | 3 | 15 | 31 January 2024 | 15 February 2024 | 132.0 | 168.0 | 0.0 | 4.0 | 3.0 | 175.0 | 175.0 | 0.0 | 57.0 | 0.0 | - | 4 | 15 | 15 February 2024 | 15 February 2024 | 7.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | + | 3 | 15 | 31 January 2024 | 15 February 2024 | 125.0 | 168.0 | 0.0 | 4.0 | 3.0 | 175.0 | 175.0 | 0.0 | 57.0 | 0.0 | + | 4 | 15 | 15 February 2024 | 15 February 2024 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | Then Loan Repayment schedule has the following data in Total row: | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | 543.0 | 0.0 | 8.0 | 6.0 | 557.0 | 557.0 | 0.0 | 189.0 | 0.0 | @@ -2844,3 +2844,131 @@ Feature: LoanChargeback | LIABILITY | 145023 | Suspense/Clearing account | | 280.0 | | EXPENSE | 744007 | Credit Loss/Bad Debt | 250.0 | | | INCOME | 404008 | Fee Charge Off | 30.0 | | + + @TestRailId:C3111 @creditAllocation @AdvancedPaymentAllocationChargeback @AdvancedPaymentAllocation + Scenario: Verify chargeback function for advanced payment allocation - credit allocation accounting entries UC10: chargeback on overpaid loan + 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 | + | PIN4_DOWNPAYMENT_ADV_PMT_ALLOC_PROG_SCHEDULE_HOR_INST_LVL_DELINQUENCY_CREDIT_ALLOCATION | 01 January 2024 | 100 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 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 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 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 0 | 01 January 2024 | | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | + | 2 | 31 | 01 February 2024 | | 50.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | + | 3 | 29 | 01 March 2024 | | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | + | 4 | 31 | 01 April 2024 | | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | + When Admin adds "LOAN_NSF_FEE" due date charge with "05 January 2024" due date and 5 EUR transaction amount + 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 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 0 | 01 January 2024 | | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | + | 2 | 31 | 01 February 2024 | | 50.0 | 25.0 | 0.0 | 0.0 | 5.0 | 30.0 | 0.0 | 0.0 | 0.0 | 30.0 | + | 3 | 29 | 01 March 2024 | | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | + | 4 | 31 | 01 April 2024 | | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | + When Admin sets the business date to "01 February 2024" + And Customer makes "AUTOPAY" repayment on "01 February 2024" with 105 EUR transaction amount + 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 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 0 | 01 January 2024 | 01 February 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 25.0 | 0.0 | + | 2 | 31 | 01 February 2024 | 01 February 2024 | 50.0 | 25.0 | 0.0 | 0.0 | 5.0 | 30.0 | 30.0 | 0.0 | 0.0 | 0.0 | + | 3 | 29 | 01 March 2024 | 01 February 2024 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + | 4 | 31 | 01 April 2024 | 01 February 2024 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | + | 01 February 2024 | Repayment | 105.0 | 100.0 | 0.0 | 0.0 | 5.0 | 0.0 | + | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | + Then Loan has 0 outstanding amount + Then Loan status will be "CLOSED_OBLIGATIONS_MET" + When Admin sets the business date to "01 March 2024" + When Admin makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "19 January 2024" with 10 EUR transaction amount + 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 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 0 | 01 January 2024 | 01 February 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 25.0 | 0.0 | + | 2 | 31 | 01 February 2024 | 01 February 2024 | 50.0 | 25.0 | 0.0 | 0.0 | 5.0 | 30.0 | 30.0 | 0.0 | 0.0 | 0.0 | + | 3 | 29 | 01 March 2024 | 01 February 2024 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + | 4 | 31 | 01 April 2024 | 01 February 2024 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 | + | 19 January 2024 | Merchant Issued Refund | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 90.0 | 0.0 | + | 01 February 2024 | Repayment | 105.0 | 90.0 | 0.0 | 0.0 | 5.0 | 0.0 | 10.0 | + | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 | + Then Loan has 0 outstanding amount + Then Loan has 10 overpaid amount + Then Loan status will be "OVERPAID" + When Admin sets the business date to "15 April 2024" + And Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 7 EUR transaction amount for Payment nr. 1 + 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 | + | ASSET | 112601 | Loans Receivable | 100.0 | | + | LIABILITY | 145023 | Suspense/Clearing account | | 100.0 | + Then Loan Transactions tab has a "MERCHANT_ISSUED_REFUND" transaction with date "19 January 2024" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | ASSET | 112601 | Loans Receivable | | 10.0 | + | LIABILITY | 145023 | Suspense/Clearing account | 10.0 | | + Then Loan Transactions tab has a "REPAYMENT" transaction with date "01 February 2024" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | ASSET | 112601 | Loans Receivable | | 90.0 | + | ASSET | 112603 | Interest/Fee Receivable | | 5.0 | + | LIABILITY | l1 | Overpayment account | | 10.0 | + | LIABILITY | 145023 | Suspense/Clearing account | 105.0 | | + Then Loan Repayment schedule has 5 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 | 0 | 01 January 2024 | 01 February 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 25.0 | 0.0 | + | 2 | 31 | 01 February 2024 | 01 February 2024 | 50.0 | 25.0 | 0.0 | 0.0 | 5.0 | 30.0 | 30.0 | 0.0 | 0.0 | 0.0 | + | 3 | 29 | 01 March 2024 | 01 February 2024 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + | 4 | 31 | 01 April 2024 | 01 February 2024 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + | 5 | 14 | 15 April 2024 | 15 April 2024 | 0.0 | 2.0 | 0.0 | 0.0 | 5.0 | 7.0 | 7.0 | 0.0 | 0.0 | 0.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 | + | 19 January 2024 | Merchant Issued Refund | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 90.0 | 0.0 | + | 01 February 2024 | Repayment | 105.0 | 90.0 | 0.0 | 0.0 | 5.0 | 0.0 | 10.0 | + | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 | + | 15 April 2024 | Chargeback | 7.0 | 2.0 | 0.0 | 0.0 | 5.0 | 0.0 | 7.0 | + Then Loan Transactions tab has a "CHARGEBACK" transaction with date "15 April 2024" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | LIABILITY | l1 | Overpayment account | 7.0 | | + | LIABILITY | 145023 | Suspense/Clearing account | | 7.0 | + Then Loan has 0 outstanding amount + Then Loan has 3 overpaid amount + Then Loan status will be "OVERPAID" + When Admin sets the business date to "16 April 2024" + When Admin makes "REPAYMENT_ADJUSTMENT_REFUND" chargeback with 7 EUR transaction amount for Payment nr. 1 + Then Loan Repayment schedule has 5 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 | 0 | 01 January 2024 | 01 February 2024 | 75.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 0.0 | 25.0 | 0.0 | + | 2 | 31 | 01 February 2024 | 01 February 2024 | 50.0 | 25.0 | 0.0 | 0.0 | 5.0 | 30.0 | 30.0 | 0.0 | 0.0 | 0.0 | + | 3 | 29 | 01 March 2024 | 01 February 2024 | 25.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + | 4 | 31 | 01 April 2024 | 01 February 2024 | 0.0 | 25.0 | 0.0 | 0.0 | 0.0 | 25.0 | 25.0 | 25.0 | 0.0 | 0.0 | + | 5 | 15 | 16 April 2024 | | 0.0 | 9.0 | 0.0 | 0.0 | 5.0 | 14.0 | 10.0 | 0.0 | 0.0 | 4.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment | + | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 | + | 19 January 2024 | Merchant Issued Refund | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 90.0 | 0.0 | + | 01 February 2024 | Repayment | 105.0 | 90.0 | 0.0 | 0.0 | 5.0 | 0.0 | 10.0 | + | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 | + | 15 April 2024 | Chargeback | 7.0 | 2.0 | 0.0 | 0.0 | 5.0 | 0.0 | 7.0 | + | 16 April 2024 | Chargeback | 7.0 | 7.0 | 0.0 | 0.0 | 0.0 | 4.0 | 3.0 | + Then Loan Transactions tab has a "CHARGEBACK" transaction with date "16 April 2024" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | LIABILITY | l1 | Overpayment account | 3.0 | | + | ASSET | 112601 | Loans Receivable | 4.0 | | + | LIABILITY | 145023 | Suspense/Clearing account | | 7.0 | + Then Loan has 4 outstanding amount + Then Loan status will be "ACTIVE" diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index e5266b169..6c07e948e 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -134,6 +134,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; +import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; @@ -712,7 +713,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { ChangedTransactionDetail changedTransactionDetail = null; final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -875,7 +876,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { * Consider removing this block of code or logically completing it for the future by getting the list of * affected Transactions ***/ - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); } @@ -943,7 +944,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { * Consider removing this block of code or logically completing it for the future by getting the list of * affected Transactions ***/ - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); } else { @@ -1137,7 +1138,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { * Consider removing this block of code or logically completing it for the future by getting the list of * affected Transactions */ - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); } else { @@ -2696,7 +2697,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { private ChangedTransactionDetail reprocessTransactionForDisbursement() { ChangedTransactionDetail changedTransactionDetail = null; if (this.loanProduct.isMultiDisburseLoan()) { - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); if (!allNonContraTransactionsPostDisbursement.isEmpty()) { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); @@ -3369,7 +3370,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); } - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -3429,12 +3430,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return incomePostTransactions; } - private List retrieveListOfTransactionsPostDisbursement() { + public List retrieveListOfTransactionsForReprocessing() { final List repaymentsOrWaivers = new ArrayList<>(); List trans = getLoanTransactions(); for (final LoanTransaction transaction : trans) { - if (transaction.isNotReversed() && (transaction.isChargeOff() || transaction.isReAge() || transaction.isReAmortize() - || !transaction.isNonMonetaryTransaction())) { + if (transaction.isNotReversed() && !transaction.isAccrual() && (transaction.isChargeOff() || transaction.isReAge() + || transaction.isReAmortize() || !transaction.isNonMonetaryTransaction())) { repaymentsOrWaivers.add(transaction); } } @@ -3818,7 +3819,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, this); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); } @@ -3868,10 +3869,15 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { } if (loanTransaction.isRefund() || loanTransaction.isRefundForActiveLoan()) { totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getAmount(currency)); - } else if (loanTransaction.isCreditBalanceRefund() || loanTransaction.isChargeback()) { + } else if (loanTransaction.isCreditBalanceRefund()) { if (loanTransaction.getPrincipalPortion(currency).isZero()) { totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getOverPaymentPortion(currency)); } + } else if (loanTransaction.isChargeback()) { + if (loanTransaction.getPrincipalPortion(currency).isZero() && getCreditAllocationRules().stream() + .filter(car -> car.getTransactionType().equals(CreditAllocationTransactionType.CHARGEBACK)).findAny().isEmpty()) { + totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getOverPaymentPortion(currency)); + } } } @@ -3972,7 +3978,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); } - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -5420,7 +5426,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions( getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); @@ -5574,7 +5580,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { public ChangedTransactionDetail processTransactions() { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions( getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); @@ -5818,7 +5824,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { public void processPostDisbursementTransactions() { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); final List copyTransactions = new ArrayList<>(); if (!allNonContraTransactionsPostDisbursement.isEmpty()) { for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) { @@ -6428,7 +6434,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, new TransactionCtx(getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges(), new MoneyHolder(getTotalOverpaidAsMoney()))); } else { - final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); + final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing(); changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index e1f12c30d..2e0830012 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -356,6 +356,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep (ctx.getInstallments().size() + 1), pastDueDate, transactionDate, zeroMoney.getAmount(), zeroMoney.getAmount(), zeroMoney.getAmount(), zeroMoney.getAmount(), false, null); recognizeAmountsAfterChargeback(ctx.getCurrency(), transactionDate, installment, chargebackAllocation); + installment.markAsAdditional(); loan.addLoanRepaymentScheduleInstallment(installment); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index ef43af882..02f6cd71b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -1159,7 +1159,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa + " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, " + " ls.penalty_charges_amount as penaltyChargesDue, ls.penalty_charges_completed_derived as penaltyChargesPaid, ls.penalty_charges_waived_derived as penaltyChargesWaived, " + " ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff, ls.total_paid_in_advance_derived as totalPaidInAdvanceForPeriod, " - + " ls.total_paid_late_derived as totalPaidLateForPeriod, (coalesce(ls.credits_amount,0) + coalesce(ls.credited_fee,0) + coalesce(ls.credited_penalty,0)) as totalCredits, ls.is_down_payment isDownPayment " + + " ls.total_paid_late_derived as totalPaidLateForPeriod, ls.credits_amount as principalCredits, ls.credited_fee as feeCredits, ls.credited_penalty as penaltyCredits, ls.is_down_payment isDownPayment " + " from m_loan_repayment_schedule ls "; } @@ -1230,9 +1230,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa disbursementChargeAmount, waivedChargeAmount, periods); // Add the Charge back or Credits to the initial amount to avoid negative balance - final BigDecimal credits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalCredits"); - - this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(credits); + final BigDecimal principalCredits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalCredits"); + final BigDecimal feeCredits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeCredits"); + final BigDecimal penaltyCredits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyCredits"); + final BigDecimal credits = principalCredits.add(feeCredits).add(penaltyCredits); + this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(principalCredits); totalPrincipalDisbursed = totalPrincipalDisbursed.add(disbursedAmount); @@ -1305,6 +1307,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa totalPaidInAdvance = totalPaidInAdvance.plus(totalPaidInAdvanceForPeriod); totalPaidLate = totalPaidLate.plus(totalPaidLateForPeriod); totalOutstanding = totalOutstanding.plus(totalOutstandingForPeriod); + totalCredits = totalCredits.add(credits); if (fromDate == null) { fromDate = this.lastDueDate; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reaging/LoanReAgingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reaging/LoanReAgingServiceImpl.java index 4db6b7e59..33909fa7d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reaging/LoanReAgingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reaging/LoanReAgingServiceImpl.java @@ -44,7 +44,6 @@ import org.apache.fineract.portfolio.loanaccount.api.LoanReAgingApiConstants; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter; @@ -181,8 +180,7 @@ public class LoanReAgingServiceImpl { } private void reProcessLoanTransactions(Loan loan) { - final List filteredTransactions = loan.getLoanTransactions().stream().filter(LoanTransaction::isNotReversed) - .filter(t -> t.isChargeOff() || !t.isNonMonetaryTransaction()).sorted(LoanTransactionComparator.INSTANCE).toList(); + final List filteredTransactions = loan.retrieveListOfTransactionsForReprocessing(); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory .determineProcessor(loan.transactionProcessingStrategy()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java index b12af758f..880b85663 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/reamortization/LoanReAmortizationServiceImpl.java @@ -22,7 +22,6 @@ import static java.math.BigDecimal.ZERO; import java.math.BigDecimal; import java.time.LocalDate; -import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; @@ -44,7 +43,6 @@ import org.apache.fineract.portfolio.loanaccount.api.LoanReAmortizationApiConsta import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -135,18 +133,11 @@ public class LoanReAmortizationServiceImpl { } private void reProcessLoanTransactions(Loan loan) { - final List repaymentsOrWaivers = new ArrayList<>(); - List trans = loan.getLoanTransactions(); - for (final LoanTransaction transaction : trans) { - if (transaction.isNotReversed() && (transaction.isChargeOff() || !transaction.isNonMonetaryTransaction())) { - repaymentsOrWaivers.add(transaction); - } - } - repaymentsOrWaivers.sort(LoanTransactionComparator.INSTANCE); + final List filteredTransactions = loan.retrieveListOfTransactionsForReprocessing(); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory .determineProcessor(loan.transactionProcessingStrategy()); - loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(), repaymentsOrWaivers, + loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(), filteredTransactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges()); }