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") //
+ );
+ });
+
+ }
}