FINERACT-1954: Waive null pointer and due date fix

- [x] Fix Waive due date handle
- [x] Fix null pointer exception
- [x] Integration test
This commit is contained in:
Janos Haber
2023-07-27 13:37:43 +02:00
committed by Adam Saghy
parent fe5d7cccee
commit ea172db712
6 changed files with 193 additions and 3 deletions
@@ -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);
@@ -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) {
@@ -209,7 +209,6 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
}
}
reprocessInstallments(installments, currency);
return changedTransactionDetail;
}
@@ -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() {
@@ -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();
@@ -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");
}