FINERACT-2312: full term configuration added
This commit is contained in:
+187
@@ -33,15 +33,19 @@ import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.AdvancedPaymentData;
|
||||
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdDisbursementDetails;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PaymentAllocationOrder;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PutLoansLoanIdResponse;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
@@ -53,6 +57,8 @@ import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -693,4 +699,185 @@ public class LoanDisbursementDetailsIntegrationTest {
|
||||
equalTo("error.msg.loan.disbursal.amount.can't.be.greater.than.maximum.applied.loan.amount.calculation"))
|
||||
.expectStatusCode(403).build();
|
||||
}
|
||||
|
||||
private AdvancedPaymentData createDefaultPaymentAllocation(String futureInstallmentAllocationRule) {
|
||||
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
|
||||
advancedPaymentData.setTransactionType("DEFAULT");
|
||||
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
|
||||
|
||||
List<PaymentAllocationOrder> paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
|
||||
PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST,
|
||||
PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
|
||||
PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
|
||||
PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST);
|
||||
|
||||
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
|
||||
return advancedPaymentData;
|
||||
}
|
||||
|
||||
private List<PaymentAllocationOrder> getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
|
||||
AtomicInteger integer = new AtomicInteger(1);
|
||||
return Arrays.stream(paymentAllocationTypes).map(pat -> {
|
||||
PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder();
|
||||
paymentAllocationOrder.setPaymentAllocationRule(pat.name());
|
||||
paymentAllocationOrder.setOrder(integer.getAndIncrement());
|
||||
return paymentAllocationOrder;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateLoanProductWithFullTermTrancheEnabled() {
|
||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation("NEXT_INSTALLMENT");
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
|
||||
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
|
||||
|
||||
final Integer loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
log.info("------------------LOAN PRODUCT CREATED WITH ID----------- {}", loanProductId);
|
||||
|
||||
GetLoanProductsProductIdResponse loanProduct = this.loanTransactionHelper.getLoanProduct(loanProductId);
|
||||
assertNotNull(loanProduct);
|
||||
assertEquals(true, loanProduct.getMultiDisburseLoan());
|
||||
assertEquals(true, loanProduct.getAllowFullTermForTranche());
|
||||
log.info("-------------------LOAN PRODUCT WITH allowFullTermForTranche CREATED SUCCESSFULLY-------");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateLoanProductWithFullTermTrancheOnCumulativeShouldFail() {
|
||||
final ResponseSpecification errorResponse = new ResponseSpecBuilder()
|
||||
.expectBody("userMessageGlobalisationCode", equalTo("validation.msg.validation.errors.exist"))
|
||||
.expectBody("errors[0].userMessageGlobalisationCode",
|
||||
equalTo("validation.msg.loanproduct.allowFullTermForTranche.requires.progressive.schedule.type"))
|
||||
.expectStatusCode(400).build();
|
||||
|
||||
final LoanTransactionHelper validationErrorHelper = new LoanTransactionHelper(this.requestSpec, errorResponse);
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.CUMULATIVE)
|
||||
.withAllowFullTermForTranche(true).build(null);
|
||||
|
||||
validationErrorHelper.getLoanProductId(loanProductJSON);
|
||||
log.info("-------------------LOAN PRODUCT WITH allowFullTermForTranche ON CUMULATIVE FAILED AS EXPECTED-------");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateLoanProductWithFullTermTrancheOnSingleDisburseShouldFail() {
|
||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation("NEXT_INSTALLMENT");
|
||||
|
||||
final ResponseSpecification errorResponse = new ResponseSpecBuilder()
|
||||
.expectBody("userMessageGlobalisationCode", equalTo("validation.msg.validation.errors.exist"))
|
||||
.expectBody("errors[0].userMessageGlobalisationCode",
|
||||
equalTo("validation.msg.loanproduct.allowFullTermForTranche.requires.multi.disburse.loan"))
|
||||
.expectStatusCode(400).build();
|
||||
|
||||
final LoanTransactionHelper validationErrorHelper = new LoanTransactionHelper(this.requestSpec, errorResponse);
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||
.withInterestTypeAsDecliningBalance().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
|
||||
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
|
||||
|
||||
validationErrorHelper.getLoanProductId(loanProductJSON);
|
||||
log.info("-------------------LOAN PRODUCT WITH allowFullTermForTranche ON SINGLE DISBURSE FAILED AS EXPECTED-------");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateLoanProductPreservesAllowFullTermForTranche() {
|
||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation("NEXT_INSTALLMENT");
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
|
||||
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
|
||||
|
||||
final Integer loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
log.info("------------------LOAN PRODUCT CREATED WITH ID----------- {}", loanProductId);
|
||||
|
||||
GetLoanProductsProductIdResponse loanProduct = this.loanTransactionHelper.getLoanProduct(loanProductId);
|
||||
assertNotNull(loanProduct);
|
||||
assertEquals(true, loanProduct.getAllowFullTermForTranche());
|
||||
|
||||
org.apache.fineract.client.models.PutLoanProductsProductIdRequest updateRequest = new org.apache.fineract.client.models.PutLoanProductsProductIdRequest();
|
||||
updateRequest.setDescription("Updated description");
|
||||
this.loanTransactionHelper.updateLoanProduct((long) loanProductId, updateRequest);
|
||||
|
||||
GetLoanProductsProductIdResponse updatedProduct = this.loanTransactionHelper.getLoanProduct(loanProductId);
|
||||
assertNotNull(updatedProduct);
|
||||
assertEquals(true, updatedProduct.getAllowFullTermForTranche());
|
||||
assertEquals("Updated description", updatedProduct.getDescription());
|
||||
log.info("-------------------LOAN PRODUCT UPDATE PRESERVED allowFullTermForTranche FLAG-------");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanInheritsAllowFullTermForTrancheFromProduct() {
|
||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation("NEXT_INSTALLMENT");
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
|
||||
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
|
||||
|
||||
final Integer loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
log.info("------------------LOAN PRODUCT CREATED WITH ID----------- {}", loanProductId);
|
||||
|
||||
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
log.info("------------------CLIENT CREATED WITH ID----------- {}", clientId);
|
||||
|
||||
List<HashMap> createTranches = new ArrayList<>();
|
||||
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 March 2014", "5000"));
|
||||
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 April 2014", "5000"));
|
||||
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("10000").withLoanTermFrequency("12")
|
||||
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
|
||||
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("1").withExpectedDisbursementDate("01 March 2014")
|
||||
.withTranches(createTranches).withSubmittedOnDate("01 March 2014")
|
||||
.withRepaymentStrategy(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)
|
||||
.build(clientId.toString(), loanProductId.toString(), null);
|
||||
|
||||
final Integer loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
log.info("------------------LOAN CREATED WITH ID----------- {}", loanId);
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
|
||||
assertNotNull(loanDetails);
|
||||
assertEquals(true, loanDetails.getAllowFullTermForTranche());
|
||||
log.info("-------------------LOAN INHERITED allowFullTermForTranche FROM PRODUCT SUCCESSFULLY-------");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanLevelOverrideOfAllowFullTermForTranche() {
|
||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation("NEXT_INSTALLMENT");
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
|
||||
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
|
||||
|
||||
final Integer loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
log.info("------------------LOAN PRODUCT CREATED WITH ID----------- {}", loanProductId);
|
||||
|
||||
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
log.info("------------------CLIENT CREATED WITH ID----------- {}", clientId);
|
||||
|
||||
List<HashMap> createTranches = new ArrayList<>();
|
||||
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 March 2014", "5000"));
|
||||
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 April 2014", "5000"));
|
||||
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("10000").withLoanTermFrequency("12")
|
||||
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
|
||||
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("1").withExpectedDisbursementDate("01 March 2014")
|
||||
.withTranches(createTranches).withSubmittedOnDate("01 March 2014")
|
||||
.withRepaymentStrategy(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY).withAllowFullTermForTranche(false)
|
||||
.build(clientId.toString(), loanProductId.toString(), null);
|
||||
|
||||
final Integer loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
log.info("------------------LOAN CREATED WITH ID----------- {}", loanId);
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
|
||||
assertNotNull(loanDetails);
|
||||
assertEquals(false, loanDetails.getAllowFullTermForTranche());
|
||||
log.info("-------------------LOAN LEVEL OVERRIDE OF allowFullTermForTranche WORKED SUCCESSFULLY-------");
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -88,6 +88,7 @@ public class LoanApplicationTestBuilder {
|
||||
private boolean enableDownPayment = false;
|
||||
private boolean enableAutoRepaymentForDownPayment = false;
|
||||
private String disbursedAmountPercentageDownPayment;
|
||||
private Boolean allowFullTermForTranche = null;
|
||||
|
||||
public String build(final String clientID, final String groupID, final String loanProductId, final String savingsID) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
@@ -220,6 +221,9 @@ public class LoanApplicationTestBuilder {
|
||||
if (disbursedAmountPercentageDownPayment != null) {
|
||||
map.put("disbursedAmountPercentageDownPayment", disbursedAmountPercentageDownPayment);
|
||||
}
|
||||
if (allowFullTermForTranche != null) {
|
||||
map.put("allowFullTermForTranche", allowFullTermForTranche);
|
||||
}
|
||||
LOG.info("Loan Application request : {} ", map);
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
@@ -468,4 +472,9 @@ public class LoanApplicationTestBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTestBuilder withAllowFullTermForTranche(final Boolean allowFullTermForTranche) {
|
||||
this.allowFullTermForTranche = allowFullTermForTranche;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
@@ -113,6 +113,7 @@ public class LoanProductTestBuilder {
|
||||
private Account feeAndPenaltyAssetAccount;
|
||||
|
||||
private Boolean multiDisburseLoan = false;
|
||||
private Boolean allowFullTermForTranche = false;
|
||||
private final String outstandingLoanBalance = "35000";
|
||||
private String maxTrancheCount = "3";
|
||||
private Boolean disallowExpectedDisbursements = false;
|
||||
@@ -225,6 +226,7 @@ public class LoanProductTestBuilder {
|
||||
}
|
||||
if (this.multiDisburseLoan) {
|
||||
map.put("multiDisburseLoan", this.multiDisburseLoan);
|
||||
map.put("allowFullTermForTranche", this.allowFullTermForTranche);
|
||||
map.put("maxTrancheCount", this.maxTrancheCount);
|
||||
map.put("outstandingLoanBalance", this.outstandingLoanBalance);
|
||||
map.put("disallowExpectedDisbursements", this.disallowExpectedDisbursements);
|
||||
@@ -242,6 +244,10 @@ public class LoanProductTestBuilder {
|
||||
map.put("maxTrancheCount", this.maxTrancheCount);
|
||||
map.put("outstandingLoanBalance", this.outstandingLoanBalance);
|
||||
}
|
||||
// Always send allowFullTermForTranche when it's true (for validation testing of single-disburse scenarios)
|
||||
if (this.allowFullTermForTranche && !this.multiDisburseLoan) {
|
||||
map.put("allowFullTermForTranche", this.allowFullTermForTranche);
|
||||
}
|
||||
|
||||
if (this.fullAccountingConfig != null) {
|
||||
map.putAll(this.fullAccountingConfig.toMap());
|
||||
@@ -742,6 +748,11 @@ public class LoanProductTestBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withAllowFullTermForTranche(boolean allowFullTermForTranche) {
|
||||
this.allowFullTermForTranche = allowFullTermForTranche;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withFeeToIncomeAccountMapping(final Long chargeId, final Long accountId) {
|
||||
if (this.feeToIncomeAccountMappings == null) {
|
||||
this.feeToIncomeAccountMappings = new ArrayList<>();
|
||||
|
||||
Reference in New Issue
Block a user