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 ea172db71 FINERACT-1954: Waive null pointer and due date fix - [x] Fix
Waive due date handle - [x] Fix null pointer exception - [x] Integration test
ea172db71 is described below
commit ea172db712ea63f1f23e9b6f4f5ba90967d719bd
Author: Janos Haber <[email protected]>
AuthorDate: Fri Jul 21 15:56:07 2023 +0200
FINERACT-1954: Waive null pointer and due date fix
- [x] Fix Waive due date handle
- [x] Fix null pointer exception
- [x] Integration test
---
.../portfolio/loanaccount/domain/Loan.java | 7 +-
.../loanaccount/domain/LoanInstallmentCharge.java | 3 +-
...tLoanRepaymentScheduleTransactionProcessor.java | 1 -
.../LoanChargeSpecificDueDateTest.java | 151 +++++++++++++++++++++
.../common/charges/ChargesHelper.java | 23 ++++
.../common/loans/LoanTransactionHelper.java | 11 ++
6 files changed, 193 insertions(+), 3 deletions(-)
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 9c62a71a1..7d78c4a1a 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
@@ -1081,7 +1081,12 @@ public class Loan extends
AbstractAuditableWithUTCDateTimeCustom {
transactionDate = loanCharge.getDueLocalDate();
}
} else if (loanCharge.isInstalmentFee()) {
- transactionDate =
loanCharge.getInstallmentLoanCharge(loanInstallmentNumber).getRepaymentInstallment().getDueDate();
+ LocalDate repaymentDueDate =
loanCharge.getInstallmentLoanCharge(loanInstallmentNumber).getRepaymentInstallment().getDueDate();
+ if (repaymentDueDate.isAfter(DateUtils.getBusinessLocalDate())) {
+ transactionDate = DateUtils.getBusinessLocalDate();
+ } else {
+ transactionDate = repaymentDueDate;
+ }
}
scheduleGeneratorDTO.setRecalculateFrom(transactionDate);
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
index 1ed06d2a6..5637d5f59 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
@@ -25,6 +25,7 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+import org.apache.fineract.interoperation.util.MathUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import
org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
@@ -168,7 +169,7 @@ public class LoanInstallmentCharge extends
AbstractPersistableCustom implements
}
public boolean isPending() {
- return !(isPaid() || isWaived());
+ return !MathUtil.isZero(this.amountOutstanding);
}
public boolean isChargeAmountpaid(MonetaryCurrency currency) {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index 5be6b1f75..039da3468 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -209,7 +209,6 @@ public abstract class
AbstractLoanRepaymentScheduleTransactionProcessor implemen
}
}
reprocessInstallments(installments, currency);
-
return changedTransactionDetail;
}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
index 1b3695ed3..442354da0 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
@@ -20,6 +20,7 @@ package org.apache.fineract.integrationtests;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
@@ -30,10 +31,12 @@ import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
+import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import
org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.JournalEntryTransactionItem;
import org.apache.fineract.client.models.PostChargesResponse;
import
org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
@@ -233,6 +236,154 @@ public class LoanChargeSpecificDueDateTest {
}
+ @Test
+ public void testApplyAndWaiveInstallmentFee() {
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.TRUE);
+ final LocalDate todaysDate = Utils.getLocalDateOfTenant();
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, todaysDate);
+
+ // Client and Loan account creation
+ final Integer clientId = ClientHelper.createClient(this.requestSpec,
this.responseSpec, "01 January 2012");
+ final GetLoanProductsProductIdResponse getLoanProductsProductResponse
= createLoanProduct(loanTransactionHelper, null);
+ assertNotNull(getLoanProductsProductResponse);
+
+ // Older date to have more than one overdue installment
+ LocalDate transactionDate = todaysDate;
+ String operationDate = Utils.dateFormatter.format(transactionDate);
+ log.info("Operation date {}", transactionDate);
+
+ // Create Loan Account
+ final Integer loanId = createLoanAccount(loanTransactionHelper,
clientId.toString(),
+ getLoanProductsProductResponse.getId().toString(),
operationDate, "1", "0");
+
+ // Get loan details
+ GetLoansLoanIdResponse getLoansLoanIdResponse =
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ validateLoanAccount(getLoansLoanIdResponse,
Double.valueOf(principalAmount), Double.valueOf("0.00"), false);
+
+ // Apply Loan Charge with specific due date
+ final String feeAmount = "1.500000";
+ String payloadJSON = ChargesHelper.getLoanSpecificInstallmentFeeJSON();
+ final PostChargesResponse postChargesResponse =
ChargesHelper.createLoanCharge(requestSpec, responseSpec, payloadJSON);
+ assertNotNull(postChargesResponse);
+ final Long chargeId = postChargesResponse.getResourceId();
+ assertNotNull(chargeId);
+
+ float amount = Float.parseFloat("5.00");
+ PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse =
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+ loanId);
+
+ payloadJSON =
LoanTransactionHelper.getSpecifiedInstallmentChargesForLoanAsJSON(chargeId.toString(),
feeAmount);
+ PostLoansLoanIdChargesResponse postLoansLoanIdChargesResponse =
loanTransactionHelper.addChargeForLoan(loanId, payloadJSON,
+ responseSpec);
+ assertNotNull(postLoansLoanIdChargesResponse);
+ final Long loanChargeId =
postLoansLoanIdChargesResponse.getResourceId();
+ assertNotNull(loanChargeId);
+
+ getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec,
responseSpec, loanId);
+ validateLoanAccount(getLoansLoanIdResponse,
Double.valueOf(principalAmount), Double.valueOf("10.00"), false);
+
+ // Waive the Loan Charge
+ final PostLoansLoanIdChargesChargeIdResponse
postWaiveLoanChargesResponse =
loanTransactionHelper.applyLoanChargeCommand(loanId,
+ loanChargeId, "waive", Utils.emptyJson());
+ assertNotNull(postWaiveLoanChargesResponse);
+
+ // evaluate the outstanding
+ getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec,
responseSpec, loanId);
+ validateLoanAccount(getLoansLoanIdResponse,
Double.valueOf(principalAmount), Double.valueOf("0.00"), false);
+
+ Optional<GetLoansLoanIdTransactions> waiveTransaction =
getLoansLoanIdResponse.getTransactions().stream()
+ .filter(transaction -> transaction.getType().getWaiveCharges()
!= null && transaction.getType().getWaiveCharges())
+ .findFirst();
+ assertTrue(waiveTransaction.isPresent());
+ assertEquals(transactionDate, waiveTransaction.get().getDate());
+
+ // Make a full repayment to close the Loan
+ amount = Float.parseFloat("1000.00");
+ loanIdTransactionsResponse =
loanTransactionHelper.makeLoanRepayment(operationDate, amount, loanId);
+ assertNotNull(loanIdTransactionsResponse);
+ log.info("Loan Transaction Id: {} {}", loanId,
loanIdTransactionsResponse.getResourceId());
+
+ getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec,
responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse,
"loanStatusType.closed.obligations.met");
+
+ }
+
+ @Test
+ public void testApplyAndWaiveInstallmentFeeAnotherDueDate() {
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.TRUE);
+ final LocalDate todaysDate = Utils.getLocalDateOfTenant();
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, todaysDate);
+
+ // Client and Loan account creation
+ final Integer clientId = ClientHelper.createClient(this.requestSpec,
this.responseSpec, "01 January 2012");
+ final GetLoanProductsProductIdResponse getLoanProductsProductResponse
= createLoanProduct(loanTransactionHelper, null);
+ assertNotNull(getLoanProductsProductResponse);
+
+ // Older date to have more than one overdue installment
+ LocalDate transactionDate = todaysDate;
+ String operationDate = Utils.dateFormatter.format(transactionDate);
+ log.info("Operation date {}", transactionDate);
+
+ // Create Loan Account
+ final Integer loanId = createLoanAccount(loanTransactionHelper,
clientId.toString(),
+ getLoanProductsProductResponse.getId().toString(),
operationDate, "1", "0");
+
+ // Get loan details
+ GetLoansLoanIdResponse getLoansLoanIdResponse =
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+ validateLoanAccount(getLoansLoanIdResponse,
Double.valueOf(principalAmount), Double.valueOf("0.00"), false);
+
+ // Apply Loan Charge with specific due date
+ final String feeAmount = "1.500000";
+ String payloadJSON = ChargesHelper.getLoanSpecificInstallmentFeeJSON();
+ final PostChargesResponse postChargesResponse =
ChargesHelper.createLoanCharge(requestSpec, responseSpec, payloadJSON);
+ assertNotNull(postChargesResponse);
+ final Long chargeId = postChargesResponse.getResourceId();
+ assertNotNull(chargeId);
+
+ float amount = Float.parseFloat("5.00");
+ PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse =
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+ loanId);
+ transactionDate = todaysDate.plusDays(32);
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, transactionDate);
+
+ payloadJSON =
LoanTransactionHelper.getSpecifiedInstallmentChargesForLoanAsJSON(chargeId.toString(),
feeAmount);
+ PostLoansLoanIdChargesResponse postLoansLoanIdChargesResponse =
loanTransactionHelper.addChargeForLoan(loanId, payloadJSON,
+ responseSpec);
+ assertNotNull(postLoansLoanIdChargesResponse);
+ final Long loanChargeId =
postLoansLoanIdChargesResponse.getResourceId();
+ assertNotNull(loanChargeId);
+
+ getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec,
responseSpec, loanId);
+ validateLoanAccount(getLoansLoanIdResponse,
Double.valueOf(principalAmount), Double.valueOf("10.00"), false);
+ LocalDate repaymentDueDate =
getLoansLoanIdResponse.getRepaymentSchedule().getPeriods().get(1).getDueDate();
+ // Waive the Loan Charge
+ final PostLoansLoanIdChargesChargeIdResponse
postWaiveLoanChargesResponse =
loanTransactionHelper.applyLoanChargeCommand(loanId,
+ loanChargeId, "waive", Utils.emptyJson());
+ assertNotNull(postWaiveLoanChargesResponse);
+
+ // evaluate the outstanding
+ getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec,
responseSpec, loanId);
+ validateLoanAccount(getLoansLoanIdResponse,
Double.valueOf(principalAmount), Double.valueOf("0.00"), false);
+
+ Optional<GetLoansLoanIdTransactions> waiveTransaction =
getLoansLoanIdResponse.getTransactions().stream()
+ .filter(transaction -> transaction.getType().getWaiveCharges()
!= null && transaction.getType().getWaiveCharges())
+ .findFirst();
+ assertTrue(waiveTransaction.isPresent());
+ assertEquals(repaymentDueDate, waiveTransaction.get().getDate());
+
+ // Make a full repayment to close the Loan
+ amount = Float.parseFloat("1000.00");
+ loanIdTransactionsResponse =
loanTransactionHelper.makeLoanRepayment(operationDate, amount, loanId);
+ assertNotNull(loanIdTransactionsResponse);
+ log.info("Loan Transaction Id: {} {}", loanId,
loanIdTransactionsResponse.getResourceId());
+
+ getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec,
responseSpec, loanId);
+ assertNotNull(getLoansLoanIdResponse);
+ loanTransactionHelper.validateLoanStatus(getLoansLoanIdResponse,
"loanStatusType.closed.obligations.met");
+
+ }
+
@Test
public void
testApplyAndWaiveLoanSpecificDueDatePenaltyWithDisbursementDate() {
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java
index 3d1013a90..d554d3b02 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java
@@ -237,6 +237,19 @@ public final class ChargesHelper extends IntegrationTest {
return chargesCreateJson;
}
+ public static String getLoanSpecificInstallmentFeeJSON() {
+ final HashMap<String, Object> map = populateDefaultsForLoan();
+ map.put("chargeTimeType", CHARGE_INSTALLMENT_FEE);
+ map.put("chargePaymentMode", CHARGE_PAYMENT_MODE_REGULAR);
+ map.put("penalty", false);
+ map.put("amount", "1.500000");
+ map.put("chargeCalculationType",
CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT_AND_INTEREST);
+
+ String chargesCreateJson = new Gson().toJson(map);
+ LOG.info("{}", chargesCreateJson);
+ return chargesCreateJson;
+ }
+
public static String getLoanSpecifiedDueDateWithAccountTransferJSON(final
Integer chargeCalculationType, final String amount,
boolean penalty) {
return getLoanSpecifiedDueDateJSON(chargeCalculationType, amount,
penalty, ChargesHelper.CHARGE_PAYMENT_MODE_ACCOUNT_TRANSFER);
@@ -251,6 +264,16 @@ public final class ChargesHelper extends IntegrationTest {
return getLoanInstallmentJSON(chargeCalculationType, amount, penalty,
ChargesHelper.CHARGE_PAYMENT_MODE_REGULAR);
}
+ public static String getLoanOverdueInstallmentJSON(final String amount) {
+ final HashMap<String, Object> map = populateDefaultsForLoan();
+ map.put("chargeId", CHARGE_OVERDUE_INSTALLMENT_FEE);
+ map.put("amount", amount);
+
+ String chargesCreateJson = new Gson().toJson(map);
+ LOG.info("{}", chargesCreateJson);
+ return chargesCreateJson;
+ }
+
public static String getLoanInstallmentJSON(final Integer
chargeCalculationType, final String amount, final boolean penalty,
final Integer paymentMode) {
final HashMap<String, Object> map = populateDefaultsForLoan();
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 46fa15454..8a774dcc1 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -1206,6 +1206,17 @@ public class LoanTransactionHelper extends
IntegrationTest {
return json;
}
+ public static String getSpecifiedInstallmentChargesForLoanAsJSON(final
String chargeId, final String amount) {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("locale", "en_GB");
+ map.put("dateFormat", "dd MMMM yyyy");
+ map.put("amount", amount);
+ map.put("chargeId", chargeId);
+ String json = new Gson().toJson(map);
+ log.info("{}", json);
+ return json;
+ }
+
public static String getDisbursementChargesForLoanAsJSON(final String
chargeId) {
return getDisbursementChargesForLoanAsJSON(chargeId, "100");
}