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


The following commit(s) were added to refs/heads/develop by this push:
     new 40a65eda1 FINERACT-1971: Fix final accrual in case of waived charge
40a65eda1 is described below

commit 40a65eda1510147ebe229a956b2f7da15078b164
Author: Adam Saghy <[email protected]>
AuthorDate: Tue Apr 16 15:24:52 2024 +0200

    FINERACT-1971: Fix final accrual in case of waived charge
---
 .../domain/LoanAccountDomainServiceJpa.java        |  31 +++--
 .../integrationtests/LoanWaiveChargeTest.java      | 127 +++++++++++++++++++++
 2 files changed, 150 insertions(+), 8 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index d6c542e28..b44af4d2d 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -936,18 +936,33 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
             Money penaltyPortion = Money.zero(currency);
 
             for (LoanRepaymentScheduleInstallment 
loanRepaymentScheduleInstallment : loan.getRepaymentScheduleInstallments()) {
+                // TODO: test with interest waiving
                 interestPortion = 
interestPortion.add(loanRepaymentScheduleInstallment.getInterestCharged(currency))
                         
.minus(loanRepaymentScheduleInstallment.getInterestAccrued(currency))
                         
.minus(loanRepaymentScheduleInstallment.getInterestWaived(currency));
-                feePortion = 
feePortion.add(loanRepaymentScheduleInstallment.getFeeChargesCharged(currency))
-                        
.minus(loanRepaymentScheduleInstallment.getFeeAccrued(currency))
-                        
.minus(loanRepaymentScheduleInstallment.getFeeChargesWaived(currency))
-                        
.minus(loanRepaymentScheduleInstallment.getCreditedFee(currency));
-                penaltyPortion = 
penaltyPortion.add(loanRepaymentScheduleInstallment.getPenaltyChargesCharged(currency))
-                        
.minus(loanRepaymentScheduleInstallment.getPenaltyAccrued(currency))
-                        
.minus(loanRepaymentScheduleInstallment.getPenaltyChargesWaived(currency))
-                        
.minus(loanRepaymentScheduleInstallment.getCreditedPenalty(currency));
             }
+
+            for (LoanCharge loanCharge : loan.getLoanCharges()) {
+                if (!loanCharge.isActive()) {
+                    continue;
+                }
+                BigDecimal accruedAmount = BigDecimal.ZERO;
+                BigDecimal waivedAmount = BigDecimal.ZERO;
+                for (LoanChargePaidBy loanChargePaidBy : 
loanCharge.getLoanChargePaidBySet()) {
+                    if (loanChargePaidBy.getLoanTransaction().isAccrual()) {
+                        accruedAmount = 
accruedAmount.add(loanChargePaidBy.getLoanTransaction().getAmount());
+                    } else if 
(loanChargePaidBy.getLoanTransaction().isChargesWaiver()) {
+                        waivedAmount = 
waivedAmount.add(loanChargePaidBy.getLoanTransaction().getAmount());
+                    }
+                }
+                Money needToAccrueAmount = 
MathUtil.negativeToZero(loanCharge.getAmount(currency).minus(accruedAmount).minus(waivedAmount));
+                if (loanCharge.isPenaltyCharge()) {
+                    penaltyPortion = penaltyPortion.add(needToAccrueAmount);
+                } else if (loanCharge.isFeeCharge()) {
+                    feePortion = feePortion.add(needToAccrueAmount);
+                }
+            }
+
             Money total = 
interestPortion.plus(feePortion).plus(penaltyPortion);
 
             if (total.isGreaterThanZero()) {
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWaiveChargeTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWaiveChargeTest.java
index 9d631d87f..8c01cccaa 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWaiveChargeTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWaiveChargeTest.java
@@ -32,6 +32,7 @@ import 
org.apache.fineract.client.models.PostLoanProductsRequest;
 import org.apache.fineract.client.models.PostLoanProductsResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
 import org.apache.fineract.client.models.PostLoansRequest;
 import org.apache.fineract.client.models.PostLoansResponse;
 import org.apache.fineract.integrationtests.common.ClientHelper;
@@ -139,4 +140,130 @@ public class LoanWaiveChargeTest extends 
BaseLoanIntegrationTest {
             assertEquals(expected, obligationsMetOnDate);
         });
     }
+
+    @ParameterizedTest
+    @MethodSource("processingStrategy")
+    public void 
accrualIsCalculatedWhenThereIsWaivedChargeAndLoanIsClosed(boolean 
advancedPaymentStrategy) {
+        double amount = 1000.0;
+        AtomicLong appliedLoanId = new AtomicLong();
+        String LoanCoBJobName = "Loan COB";
+
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            PostLoanProductsRequest product;
+            if (advancedPaymentStrategy) {
+                product = 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation();
+            } else {
+                product = 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
+            }
+
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(product);
+            Long loanProductId = loanProductResponse.getResourceId();
+
+            // Apply and Approve Loan
+
+            PostLoansRequest applicationRequest = applyLoanRequest(clientId, 
loanProductId, "01 January 2023", amount, 1);
+            if (advancedPaymentStrategy) {
+                applicationRequest = applicationRequest
+                        
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY);
+            }
+
+            PostLoansResponse postLoansResponse = 
loanTransactionHelper.applyLoan(applicationRequest);
+
+            PostLoansLoanIdResponse approvedLoanResult = 
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
+                    approveLoanRequest(amount, "01 January 2023"));
+
+            Long loanId = approvedLoanResult.getLoanId();
+            appliedLoanId.set(loanId);
+
+            // disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(amount), "01 January 
2023");
+
+            // verify schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(0.0, null, "01 January 2023"), //
+                    installment(1000.0, 0.0, 0.0, 1000.0, false, "31 January 
2023"));
+        });
+
+        runAt("10 January 2023", () -> {
+            Long loanId = appliedLoanId.get();
+
+            // create charge
+            double chargeAmount = 10.0;
+            PostChargesResponse chargeResult = createCharge(chargeAmount);
+            Long chargeId = chargeResult.getResourceId();
+
+            PostLoansLoanIdChargesResponse loanChargeResult = 
addLoanCharge(loanId, chargeId, "09 January 2023", chargeAmount);
+            loanChargeResult.getResourceId();
+            this.schedulerJobHelper.executeAndAwaitJob(LoanCoBJobName);
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(0.0, null, "01 January 2023"), //
+                    installment(1000.0, 0.0, 10.0, 1010.0, false, "31 January 
2023") //
+
+            );
+            verifyTransactions(loanId, //
+                    transaction(1000.0, "Disbursement", "01 January 2023", 
1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(10.0, "Accrual", "09 January 2023", 0.0, 0.0, 
0.0, 10.0, 0.0, 0.0, 0.0));
+        });
+        runAt("11 January 2023", () -> {
+            Long loanId = appliedLoanId.get();
+
+            // create charge
+            double chargeAmount = 9.0;
+            PostChargesResponse chargeResult = createCharge(chargeAmount);
+            Long chargeId = chargeResult.getResourceId();
+
+            PostLoansLoanIdChargesResponse loanChargeResult = 
addLoanCharge(loanId, chargeId, "10 January 2023", chargeAmount);
+            Long loanChargeId = loanChargeResult.getResourceId();
+            this.schedulerJobHelper.executeAndAwaitJob(LoanCoBJobName);
+            // waive charge
+            waiveLoanCharge(loanId, loanChargeId, 1);
+
+            verifyTransactions(loanId, //
+                    transaction(1000.0, "Disbursement", "01 January 2023", 
1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(10.0, "Accrual", "09 January 2023", 0.0, 0.0, 
0.0, 10.0, 0.0, 0.0, 0.0), //
+                    transaction(9.0, "Accrual", "10 January 2023", 0.0, 0.0, 
0.0, 9.0, 0.0, 0.0, 0.0), //
+                    transaction(9.0, "Waive loan charges", "10 January 2023", 
1000.0, 0.0, 0.0, 9.0, 0.0, 0.0, 0.0) //
+            );
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(0.0, null, "01 January 2023"), //
+                    installment(1000.0, 0.0, 19.0, 1010.0, false, "31 January 
2023") //
+            );
+        });
+
+        runAt("12 January 2023", () -> {
+            Long loanId = appliedLoanId.get();
+
+            // create charge
+            double chargeAmount = 8.0;
+            PostChargesResponse chargeResult = createCharge(chargeAmount);
+            Long chargeId = chargeResult.getResourceId();
+
+            PostLoansLoanIdChargesResponse loanChargeResult = 
addLoanCharge(loanId, chargeId, "11 January 2023", chargeAmount);
+            loanChargeResult.getResourceId();
+
+            loanTransactionHelper.makeLoanRepayment(loanId, new 
PostLoansLoanIdTransactionsRequest().transactionDate("12 January 2023")
+                    .dateFormat("dd MMMM 
yyyy").locale("en").transactionAmount(1018.0));
+
+            verifyTransactions(loanId, //
+                    transaction(1000.0, "Disbursement", "01 January 2023", 
1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(10.0, "Accrual", "09 January 2023", 0.0, 0.0, 
0.0, 10.0, 0.0, 0.0, 0.0), //
+                    transaction(9.0, "Accrual", "10 January 2023", 0.0, 0.0, 
0.0, 9.0, 0.0, 0.0, 0.0), //
+                    transaction(9.0, "Waive loan charges", "10 January 2023", 
1000.0, 0.0, 0.0, 9.0, 0.0, 0.0, 0.0), //
+                    transaction(1018.0, "Repayment", "12 January 2023", 0.0, 
1000.0, 0.0, 18.0, 0.0, 0.0, 0.0), //
+                    transaction(8.0, "Accrual", "12 January 2023", 0.0, 0.0, 
0.0, 8.0, 0.0, 0.0, 0.0) //
+            );
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(0.0, null, "01 January 2023"), //
+                    installment(1000.0, 0.0, 27.0, 0.0, true, "31 January 
2023") //
+            );
+        });
+
+    }
 }

Reply via email to