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:
+6
-1
@@ -1081,7 +1081,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
transactionDate = loanCharge.getDueLocalDate();
|
transactionDate = loanCharge.getDueLocalDate();
|
||||||
}
|
}
|
||||||
} else if (loanCharge.isInstalmentFee()) {
|
} 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);
|
scheduleGeneratorDTO.setRecalculateFrom(transactionDate);
|
||||||
|
|||||||
+2
-1
@@ -25,6 +25,7 @@ import jakarta.persistence.ManyToOne;
|
|||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
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.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
@@ -168,7 +169,7 @@ public class LoanInstallmentCharge extends AbstractPersistableCustom implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPending() {
|
public boolean isPending() {
|
||||||
return !(isPaid() || isWaived());
|
return !MathUtil.isZero(this.amountOutstanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isChargeAmountpaid(MonetaryCurrency currency) {
|
public boolean isChargeAmountpaid(MonetaryCurrency currency) {
|
||||||
|
|||||||
-1
@@ -209,7 +209,6 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
reprocessInstallments(installments, currency);
|
reprocessInstallments(installments, currency);
|
||||||
|
|
||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+151
@@ -20,6 +20,7 @@ package org.apache.fineract.integrationtests;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
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.RequestSpecBuilder;
|
||||||
import io.restassured.builder.ResponseSpecBuilder;
|
import io.restassured.builder.ResponseSpecBuilder;
|
||||||
@@ -30,10 +31,12 @@ import java.math.BigDecimal;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
|
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
|
||||||
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
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.JournalEntryTransactionItem;
|
||||||
import org.apache.fineract.client.models.PostChargesResponse;
|
import org.apache.fineract.client.models.PostChargesResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
|
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
|
@Test
|
||||||
public void testApplyAndWaiveLoanSpecificDueDatePenaltyWithDisbursementDate() {
|
public void testApplyAndWaiveLoanSpecificDueDatePenaltyWithDisbursementDate() {
|
||||||
|
|
||||||
|
|||||||
+23
@@ -237,6 +237,19 @@ public final class ChargesHelper extends IntegrationTest {
|
|||||||
return chargesCreateJson;
|
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,
|
public static String getLoanSpecifiedDueDateWithAccountTransferJSON(final Integer chargeCalculationType, final String amount,
|
||||||
boolean penalty) {
|
boolean penalty) {
|
||||||
return getLoanSpecifiedDueDateJSON(chargeCalculationType, amount, penalty, ChargesHelper.CHARGE_PAYMENT_MODE_ACCOUNT_TRANSFER);
|
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);
|
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,
|
public static String getLoanInstallmentJSON(final Integer chargeCalculationType, final String amount, final boolean penalty,
|
||||||
final Integer paymentMode) {
|
final Integer paymentMode) {
|
||||||
final HashMap<String, Object> map = populateDefaultsForLoan();
|
final HashMap<String, Object> map = populateDefaultsForLoan();
|
||||||
|
|||||||
+11
@@ -1206,6 +1206,17 @@ public class LoanTransactionHelper extends IntegrationTest {
|
|||||||
return json;
|
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) {
|
public static String getDisbursementChargesForLoanAsJSON(final String chargeId) {
|
||||||
return getDisbursementChargesForLoanAsJSON(chargeId, "100");
|
return getDisbursementChargesForLoanAsJSON(chargeId, "100");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user