Merge pull request #1600 from sachinkulkarni12/CC-36-New
Loan Repayment Rescheduling while disbursement
This commit is contained in:
+228
@@ -0,0 +1,228 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.integrationtests;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mifosplatform.integrationtests.common.ClientHelper;
|
||||||
|
import org.mifosplatform.integrationtests.common.Utils;
|
||||||
|
import org.mifosplatform.integrationtests.common.accounting.Account;
|
||||||
|
import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||||
|
import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder;
|
||||||
|
import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker;
|
||||||
|
import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper;
|
||||||
|
|
||||||
|
import com.jayway.restassured.builder.RequestSpecBuilder;
|
||||||
|
import com.jayway.restassured.builder.ResponseSpecBuilder;
|
||||||
|
import com.jayway.restassured.http.ContentType;
|
||||||
|
import com.jayway.restassured.specification.RequestSpecification;
|
||||||
|
import com.jayway.restassured.specification.ResponseSpecification;
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class LoanRepaymentRescheduleAtDisbursementTest {
|
||||||
|
|
||||||
|
private ResponseSpecification responseSpec;
|
||||||
|
private RequestSpecification requestSpec;
|
||||||
|
private LoanTransactionHelper loanTransactionHelper;
|
||||||
|
private LoanApplicationApprovalTest loanApplicationApprovalTest;
|
||||||
|
private ResponseSpecification generalResponseSpec;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
Utils.initializeRESTAssured();
|
||||||
|
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||||
|
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||||
|
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||||
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
|
this.loanApplicationApprovalTest = new LoanApplicationApprovalTest();
|
||||||
|
this.generalResponseSpec = new ResponseSpecBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void testLoanRepaymentRescheduleAtDisbursement(){
|
||||||
|
|
||||||
|
final String approvalAmount = "10000";
|
||||||
|
final String approveDate = "01 March 2015";
|
||||||
|
final String expectedDisbursementDate = "01 March 2015";
|
||||||
|
final String disbursementDate = "01 March 2015";
|
||||||
|
final String adjustRepaymentDate = "16 March 2015";
|
||||||
|
final String recalculationRestFrequencyDate = "01 January 2012";
|
||||||
|
|
||||||
|
// CREATE CLIENT
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2014");
|
||||||
|
System.out.println("---------------------------------CLIENT CREATED WITH ID---------------------------------------------------"
|
||||||
|
+ clientID);
|
||||||
|
|
||||||
|
// CREATE LOAN MULTIDISBURSAL PRODUCT WITH INTEREST RECALCULATION
|
||||||
|
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY,
|
||||||
|
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
|
||||||
|
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS,
|
||||||
|
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "0", recalculationRestFrequencyDate,
|
||||||
|
LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null);
|
||||||
|
|
||||||
|
// CREATE TRANCHES
|
||||||
|
List<HashMap> createTranches = new ArrayList<>();
|
||||||
|
createTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 March 2015", "5000"));
|
||||||
|
createTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 May 2015", "5000"));
|
||||||
|
|
||||||
|
// APPROVE TRANCHES
|
||||||
|
List<HashMap> approveTranches = new ArrayList<>();
|
||||||
|
approveTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 March 2015", "5000"));
|
||||||
|
approveTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 May 2015", "5000"));
|
||||||
|
|
||||||
|
// APPLY FOR TRANCHE LOAN WITH INTEREST RECALCULATION
|
||||||
|
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, disbursementDate,
|
||||||
|
recalculationRestFrequencyDate, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList<HashMap>(0), createTranches);
|
||||||
|
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
|
||||||
|
// VALIDATE THE LOAN STATUS
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount(approveDate, expectedDisbursementDate, approvalAmount,
|
||||||
|
loanID, approveTranches);
|
||||||
|
|
||||||
|
// VALIDATE THE LOAN IS APPROVED
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
// DISBURSE A FIRST TRANCHE
|
||||||
|
this.loanTransactionHelper.disburseLoanWithRepaymentReschedule(disbursementDate, loanID, adjustRepaymentDate);
|
||||||
|
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
|
||||||
|
ArrayList<HashMap> loanRepaymnetSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec, loanID);
|
||||||
|
HashMap firstInstallement = loanRepaymnetSchedule.get(1);
|
||||||
|
Map<String, Object> expectedvalues = new HashMap<>(3);
|
||||||
|
Calendar date = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||||
|
date.set(2015, Calendar.MARCH, 16);
|
||||||
|
expectedvalues.put("dueDate", getDateAsArray(date, 0));
|
||||||
|
expectedvalues.put("principalDue", "834.71");
|
||||||
|
expectedvalues.put("interestDue", "49.32");
|
||||||
|
expectedvalues.put("feeChargesDue", "0");
|
||||||
|
expectedvalues.put("penaltyChargesDue", "0");
|
||||||
|
expectedvalues.put("totalDueForPeriod", "884.03");
|
||||||
|
|
||||||
|
// VALIDATE REPAYMENT SCHEDULE
|
||||||
|
verifyLoanRepaymentSchedule(firstInstallement, expectedvalues);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyLoanRepaymentSchedule(final HashMap firstInstallement, final Map<String, Object> expectedvalues) {
|
||||||
|
|
||||||
|
assertEquals(expectedvalues.get("dueDate"), firstInstallement.get("dueDate"));
|
||||||
|
assertEquals(String.valueOf(expectedvalues.get("principalDue")), String.valueOf(firstInstallement.get("principalDue")));
|
||||||
|
assertEquals(String.valueOf(expectedvalues.get("interestDue")), String.valueOf(firstInstallement.get("interestDue")));
|
||||||
|
assertEquals(String.valueOf(expectedvalues.get("feeChargesDue")), String.valueOf(firstInstallement.get("feeChargesDue")));
|
||||||
|
assertEquals(String.valueOf(expectedvalues.get("penaltyChargesDue")), String.valueOf(firstInstallement.get("penaltyChargesDue")));
|
||||||
|
assertEquals(String.valueOf(expectedvalues.get("totalDueForPeriod")), String.valueOf(firstInstallement.get("totalDueForPeriod")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy,
|
||||||
|
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
||||||
|
final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval,
|
||||||
|
final String recalculationRestFrequencyDate, final String preCloseInterestCalculationStrategy, final Account[] accounts) {
|
||||||
|
final String recalculationCompoundingFrequencyType = null;
|
||||||
|
final String recalculationCompoundingFrequencyInterval = null;
|
||||||
|
final String recalculationCompoundingFrequencyDate = null;
|
||||||
|
return createLoanProductWithInterestRecalculation(repaymentStrategy, interestRecalculationCompoundingMethod,
|
||||||
|
rescheduleStrategyMethod, recalculationRestFrequencyType, recalculationRestFrequencyInterval,
|
||||||
|
recalculationRestFrequencyDate, recalculationCompoundingFrequencyType, recalculationCompoundingFrequencyInterval,
|
||||||
|
recalculationCompoundingFrequencyDate, preCloseInterestCalculationStrategy, accounts, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy,
|
||||||
|
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
||||||
|
final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval,
|
||||||
|
final String recalculationRestFrequencyDate, final String recalculationCompoundingFrequencyType,
|
||||||
|
final String recalculationCompoundingFrequencyInterval, final String recalculationCompoundingFrequencyDate,
|
||||||
|
final String preCloseInterestCalculationStrategy, final Account[] accounts, final String chargeId,
|
||||||
|
boolean isArrearsBasedOnOriginalSchedule) {
|
||||||
|
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||||
|
LoanProductTestBuilder builder = new LoanProductTestBuilder()
|
||||||
|
.withPrincipal("10000.00")
|
||||||
|
.withNumberOfRepayments("12")
|
||||||
|
.withRepaymentAfterEvery("2")
|
||||||
|
.withRepaymentTypeAsWeek()
|
||||||
|
.withinterestRatePerPeriod("2")
|
||||||
|
.withInterestRateFrequencyTypeAsMonths()
|
||||||
|
.withTranches(true)
|
||||||
|
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
|
||||||
|
.withRepaymentStrategy(repaymentStrategy)
|
||||||
|
.withInterestTypeAsDecliningBalance()
|
||||||
|
.withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod,
|
||||||
|
preCloseInterestCalculationStrategy)
|
||||||
|
.withInterestRecalculationRestFrequencyDetails(recalculationRestFrequencyType, recalculationRestFrequencyInterval,
|
||||||
|
recalculationRestFrequencyDate)
|
||||||
|
.withInterestRecalculationCompoundingFrequencyDetails(recalculationCompoundingFrequencyType,
|
||||||
|
recalculationCompoundingFrequencyInterval, recalculationCompoundingFrequencyDate);
|
||||||
|
if (accounts != null) {
|
||||||
|
builder = builder.withAccountingRulePeriodicAccrual(accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArrearsBasedOnOriginalSchedule) builder = builder.withArrearsConfiguration();
|
||||||
|
|
||||||
|
final String loanProductJSON = builder.build(chargeId);
|
||||||
|
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, final Integer loanProductID,
|
||||||
|
final String disbursementDate, final String restStartDate, final String repaymentStrategy, final List<HashMap> charges, List<HashMap> tranches) {
|
||||||
|
final String graceOnInterestPayment = null;
|
||||||
|
final String compoundingStartDate = null;
|
||||||
|
final String graceOnPrincipalPayment = null;
|
||||||
|
return applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, disbursementDate, restStartDate,
|
||||||
|
compoundingStartDate, repaymentStrategy, charges, graceOnInterestPayment, graceOnPrincipalPayment,tranches);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, final Integer loanProductID,
|
||||||
|
final String disbursementDate, final String restStartDate, final String compoundingStartDate, final String repaymentStrategy,
|
||||||
|
final List<HashMap> charges, final String graceOnInterestPayment, final String graceOnPrincipalPayment, List<HashMap> tranches) {
|
||||||
|
System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||||
|
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||||
|
.withPrincipal("10000.00") //
|
||||||
|
.withLoanTermFrequency("24") //
|
||||||
|
.withLoanTermFrequencyAsWeeks() //
|
||||||
|
.withNumberOfRepayments("12") //
|
||||||
|
.withRepaymentEveryAfter("2") //
|
||||||
|
.withRepaymentFrequencyTypeAsWeeks() //
|
||||||
|
.withInterestRatePerPeriod("2") //
|
||||||
|
.withAmortizationTypeAsEqualInstallments() //
|
||||||
|
.withTranches(tranches)
|
||||||
|
.withFixedEmiAmount("") //
|
||||||
|
.withInterestTypeAsDecliningBalance() //
|
||||||
|
.withInterestCalculationPeriodTypeAsDays() //
|
||||||
|
.withInterestCalculationPeriodTypeAsDays() //
|
||||||
|
.withExpectedDisbursementDate(disbursementDate) //
|
||||||
|
.withSubmittedOnDate(disbursementDate) //
|
||||||
|
.withRestFrequencyDate(restStartDate)//
|
||||||
|
.withwithRepaymentStrategy(repaymentStrategy) //
|
||||||
|
.withCharges(charges)//
|
||||||
|
.build(clientID.toString(), loanProductID.toString(), null);
|
||||||
|
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List getDateAsArray(Calendar date, int addPeriod) {
|
||||||
|
return getDateAsArray(date, addPeriod, Calendar.DAY_OF_MONTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List getDateAsArray(Calendar date, int addvalue, int type) {
|
||||||
|
date.add(type, addvalue);
|
||||||
|
return new ArrayList<>(Arrays.asList(date.get(Calendar.YEAR), date.get(Calendar.MONTH) + 1,
|
||||||
|
date.get(Calendar.DAY_OF_MONTH)));
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -153,6 +153,11 @@ public class LoanTransactionHelper {
|
|||||||
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date, null));
|
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HashMap disburseLoanWithRepaymentReschedule(final String date, final Integer loanID, String adjustRepaymentDate) {
|
||||||
|
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanWithRepaymentRescheduleAsJSON(date,
|
||||||
|
null, adjustRepaymentDate));
|
||||||
|
}
|
||||||
|
|
||||||
public HashMap disburseLoan(final String date, final Integer loanID, final String disburseAmt) {
|
public HashMap disburseLoan(final String date, final Integer loanID, final String disburseAmt) {
|
||||||
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date, disburseAmt));
|
return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date, disburseAmt));
|
||||||
}
|
}
|
||||||
@@ -261,6 +266,20 @@ public class LoanTransactionHelper {
|
|||||||
return new Gson().toJson(map);
|
return new Gson().toJson(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getDisburseLoanWithRepaymentRescheduleAsJSON(final String actualDisbursementDate, final String transactionAmount, final String adjustRepaymentDate) {
|
||||||
|
final HashMap<String, String> map = new HashMap<>();
|
||||||
|
map.put("locale", "en");
|
||||||
|
map.put("dateFormat", "dd MMMM yyyy");
|
||||||
|
map.put("actualDisbursementDate", actualDisbursementDate);
|
||||||
|
map.put("adjustRepaymentDate", adjustRepaymentDate);
|
||||||
|
map.put("note", "DISBURSE NOTE");
|
||||||
|
if (transactionAmount != null) {
|
||||||
|
map.put("transactionAmount", transactionAmount);
|
||||||
|
}
|
||||||
|
System.out.println("Loan Application disburse request : " + map);
|
||||||
|
return new Gson().toJson(map);
|
||||||
|
}
|
||||||
|
|
||||||
private String getApproveLoanAsJSON(final String approvalDate) {
|
private String getApproveLoanAsJSON(final String approvalDate) {
|
||||||
return getApproveLoanAsJSON(approvalDate, null, null, null);
|
return getApproveLoanAsJSON(approvalDate, null, null, null);
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-3
@@ -43,8 +43,9 @@ public class LoanTransactionData {
|
|||||||
private final BigDecimal outstandingLoanBalance;
|
private final BigDecimal outstandingLoanBalance;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private final LocalDate submittedOnDate;
|
private final LocalDate submittedOnDate;
|
||||||
|
|
||||||
private final boolean manuallyReversed;
|
private final boolean manuallyReversed;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final LocalDate possibleNextRepaymentDate;
|
||||||
|
|
||||||
// templates
|
// templates
|
||||||
final Collection<PaymentTypeData> paymentTypeOptions;
|
final Collection<PaymentTypeData> paymentTypeOptions;
|
||||||
@@ -121,16 +122,74 @@ public class LoanTransactionData {
|
|||||||
this.outstandingLoanBalance = outstandingLoanBalance;
|
this.outstandingLoanBalance = outstandingLoanBalance;
|
||||||
this.submittedOnDate = submittedOnDate;
|
this.submittedOnDate = submittedOnDate;
|
||||||
this.manuallyReversed = manuallyReversed;
|
this.manuallyReversed = manuallyReversed;
|
||||||
|
this.possibleNextRepaymentDate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanTransactionData(Long id, LoanTransactionEnumData transactionType, LocalDate date, BigDecimal totalAmount,
|
public LoanTransactionData(Long id, LoanTransactionEnumData transactionType, LocalDate date, BigDecimal totalAmount,
|
||||||
BigDecimal principalPortion, BigDecimal interestPortion, BigDecimal feeChargesPortion, BigDecimal penaltyChargesPortion,
|
BigDecimal principalPortion, BigDecimal interestPortion, BigDecimal feeChargesPortion, BigDecimal penaltyChargesPortion,
|
||||||
BigDecimal overPaymentPortion, BigDecimal unrecognizedIncomePortion, BigDecimal outstandingLoanBalance,final boolean manuallyReversed) {
|
BigDecimal overPaymentPortion, BigDecimal unrecognizedIncomePortion, BigDecimal outstandingLoanBalance,final boolean manuallyReversed) {
|
||||||
this(id, null, null, transactionType, null, null, date, totalAmount, principalPortion, interestPortion, feeChargesPortion,
|
this(id, null, null, transactionType, null, null, date, totalAmount, principalPortion, interestPortion, feeChargesPortion,
|
||||||
penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, null, null, null, null, outstandingLoanBalance, null,manuallyReversed);
|
penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, null, null, null, null, outstandingLoanBalance, null,
|
||||||
|
manuallyReversed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate dateOf() {
|
public static LoanTransactionData LoanTransactionDataForDisbursalTemplate(final LoanTransactionEnumData transactionType, final LocalDate expectedDisbursedOnLocalDateForTemplate,
|
||||||
|
final BigDecimal disburseAmountForTemplate, final Collection<PaymentTypeData> paymentOptions,
|
||||||
|
final BigDecimal retriveLastEmiAmount, final LocalDate possibleNextRepaymentDate) {
|
||||||
|
final Long id = null;
|
||||||
|
final Long officeId = null;
|
||||||
|
final String officeName = null;
|
||||||
|
final PaymentDetailData paymentDetailData = null;
|
||||||
|
final CurrencyData currency = null;
|
||||||
|
final BigDecimal unrecognizedIncomePortion = null;
|
||||||
|
final BigDecimal principalPortion = null;;
|
||||||
|
final BigDecimal interestPortion = null;
|
||||||
|
final BigDecimal feeChargesPortion = null;
|
||||||
|
final BigDecimal penaltyChargesPortion = null;
|
||||||
|
final BigDecimal overpaymentPortion = null;
|
||||||
|
final String externalId = null;
|
||||||
|
final BigDecimal outstandingLoanBalance = null;
|
||||||
|
final AccountTransferData transfer = null;
|
||||||
|
final LocalDate submittedOnDate = null;
|
||||||
|
final boolean manuallyReversed = false;
|
||||||
|
return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currency, expectedDisbursedOnLocalDateForTemplate,
|
||||||
|
disburseAmountForTemplate, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion,
|
||||||
|
paymentOptions, transfer, externalId, retriveLastEmiAmount, outstandingLoanBalance, submittedOnDate, manuallyReversed, possibleNextRepaymentDate);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanTransactionData(Long id , final Long officeId, final String officeName, LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData,
|
||||||
|
final CurrencyData currency, final LocalDate date, BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion,
|
||||||
|
final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, BigDecimal unrecognizedIncomePortion, Collection<PaymentTypeData> paymentOptions,
|
||||||
|
final AccountTransferData transfer, final String externalId, final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance,
|
||||||
|
final LocalDate submittedOnDate, final boolean manuallyReversed, final LocalDate possibleNextRepaymentDate) {
|
||||||
|
this.id = id;
|
||||||
|
this.officeId = officeId;
|
||||||
|
this.officeName = officeName;
|
||||||
|
this.type = transactionType;
|
||||||
|
this.paymentDetailData = paymentDetailData;
|
||||||
|
this.currency = currency;
|
||||||
|
this.date = date;
|
||||||
|
this.amount = amount;
|
||||||
|
this.principalPortion = principalPortion;
|
||||||
|
this.interestPortion = interestPortion;
|
||||||
|
this.feeChargesPortion = feeChargesPortion;
|
||||||
|
this.penaltyChargesPortion = penaltyChargesPortion;
|
||||||
|
this.unrecognizedIncomePortion = unrecognizedIncomePortion;
|
||||||
|
this.paymentTypeOptions = paymentOptions;
|
||||||
|
this.externalId = externalId;
|
||||||
|
this.transfer = transfer;
|
||||||
|
this.overpaymentPortion = overpaymentPortion;
|
||||||
|
this.fixedEmiAmount = fixedEmiAmount;
|
||||||
|
this.outstandingLoanBalance = outstandingLoanBalance;
|
||||||
|
this.submittedOnDate = submittedOnDate;
|
||||||
|
this.manuallyReversed = manuallyReversed;
|
||||||
|
this.possibleNextRepaymentDate = possibleNextRepaymentDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public LocalDate dateOf() {
|
||||||
return this.date;
|
return this.date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-2
@@ -2324,7 +2324,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
|
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
|
||||||
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser) {
|
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser, LocalDate nextPossibleRepaymentDate, Date rescheduledRepaymentDate) {
|
||||||
boolean isEmiAmountChanged = false;
|
boolean isEmiAmountChanged = false;
|
||||||
if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstallmentAmount()) && emiAmount != null
|
if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstallmentAmount()) && emiAmount != null
|
||||||
&& emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
&& emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
||||||
@@ -2332,13 +2332,19 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
final Date dateValue = null;
|
final Date dateValue = null;
|
||||||
final boolean isSpecificToInstallment = false;
|
final boolean isSpecificToInstallment = false;
|
||||||
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
||||||
actualDisbursementDate.toDate(), emiAmount, dateValue, isSpecificToInstallment, this);
|
actualDisbursementDate.toDate(), emiAmount, dateValue, isSpecificToInstallment, this, LoanStatus.ACTIVE.getValue());
|
||||||
this.loanTermVariations.add(loanVariationTerms);
|
this.loanTermVariations.add(loanVariationTerms);
|
||||||
} else {
|
} else {
|
||||||
this.fixedEmiAmount = emiAmount;
|
this.fixedEmiAmount = emiAmount;
|
||||||
}
|
}
|
||||||
isEmiAmountChanged = true;
|
isEmiAmountChanged = true;
|
||||||
}
|
}
|
||||||
|
if(rescheduledRepaymentDate != null && this.loanProduct.isMultiDisburseLoan()){
|
||||||
|
final boolean isSpecificToInstallment = false;
|
||||||
|
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.DUE_DATE.getValue(),
|
||||||
|
nextPossibleRepaymentDate.toDate(), emiAmount, rescheduledRepaymentDate, isSpecificToInstallment, this, LoanStatus.ACTIVE.getValue());
|
||||||
|
this.loanTermVariations.add(loanVariationTerms);
|
||||||
|
}
|
||||||
|
|
||||||
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
|
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
|
||||||
regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||||
@@ -5533,6 +5539,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
actualDisbursementDate = lastTransactionDate;
|
actualDisbursementDate = lastTransactionDate;
|
||||||
updateLoanToLastDisbursalState(actualDisbursementDate);
|
updateLoanToLastDisbursalState(actualDisbursementDate);
|
||||||
|
for (Iterator<LoanTermVariations> iterator = this.loanTermVariations.iterator(); iterator.hasNext();) {
|
||||||
|
LoanTermVariations loanTermVariations = iterator.next();
|
||||||
|
if (loanTermVariations.fetchDateValue().isAfter(actualDisbursementDate)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
reverseExistingTransactionsTillLastDisbursal(actualDisbursementDate);
|
reverseExistingTransactionsTillLastDisbursal(actualDisbursementDate);
|
||||||
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds,
|
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds,
|
||||||
currentUser);
|
currentUser);
|
||||||
@@ -5622,4 +5634,26 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
this.expectedFirstRepaymentOnDate = expectedFirstRepaymentOnDate;
|
this.expectedFirstRepaymentOnDate = expectedFirstRepaymentOnDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the next repayment date for rescheduling at the time of disbursement
|
||||||
|
*/
|
||||||
|
public LocalDate getNextPossibleRepaymentDateForRescheduling(){
|
||||||
|
Set<LoanDisbursementDetails> loanDisbursementDetails = this.disbursementDetails;
|
||||||
|
LocalDate nextRepaymentDate = new LocalDate();
|
||||||
|
for(LoanDisbursementDetails loanDisbursementDetail : loanDisbursementDetails){
|
||||||
|
if(loanDisbursementDetail.actualDisbursementDate() == null){
|
||||||
|
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
|
if (installment.getDueDate().isEqual(loanDisbursementDetail.expectedDisbursementDateAsLocalDate()) ||
|
||||||
|
installment.getDueDate().isAfter(loanDisbursementDetail.expectedDisbursementDateAsLocalDate())
|
||||||
|
&& installment.isNotFullyPaidOff()) {
|
||||||
|
nextRepaymentDate = installment.getDueDate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nextRepaymentDate;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -61,6 +61,17 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
|
|||||||
this.onLoanStatus = loan.status().getValue();
|
this.onLoanStatus = loan.status().getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
|
||||||
|
final boolean isSpecificToInstallment, final Loan loan, final Integer loanStatus) {
|
||||||
|
this.loan = loan;
|
||||||
|
this.termApplicableFrom = termApplicableFrom;
|
||||||
|
this.termType = termType;
|
||||||
|
this.decimalValue = decimalValue;
|
||||||
|
this.dateValue = dateValue;
|
||||||
|
this.isSpecificToInstallment = isSpecificToInstallment;
|
||||||
|
this.onLoanStatus = loanStatus;
|
||||||
|
}
|
||||||
|
|
||||||
protected LoanTermVariations() {
|
protected LoanTermVariations() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ public final class LoanEventApiJsonValidator {
|
|||||||
"dateFormat", LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
|
"dateFormat", LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
|
||||||
} else {
|
} else {
|
||||||
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale",
|
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale",
|
||||||
"dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber",
|
"dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber", "adjustRepaymentDate",
|
||||||
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
|
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-5
@@ -480,11 +480,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
if (paymentDetailsRequired) {
|
if (paymentDetailsRequired) {
|
||||||
paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||||
}
|
}
|
||||||
final BigDecimal outstandingLoanBalance = null;
|
return LoanTransactionData.LoanTransactionDataForDisbursalTemplate(transactionType, loan.getExpectedDisbursedOnLocalDateForTemplate(), loan.getDisburseAmountForTemplate(),
|
||||||
final BigDecimal unrecognizedIncomePortion = null;
|
paymentOptions, loan.retriveLastEmiAmount(), loan.getNextPossibleRepaymentDateForRescheduling());
|
||||||
return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDateForTemplate(),
|
|
||||||
loan.getDisburseAmountForTemplate(), null, null, null, null, null, unrecognizedIncomePortion, paymentOptions, null, null,
|
|
||||||
loan.retriveLastEmiAmount(), outstandingLoanBalance, false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-4
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -284,6 +285,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
checkClientOrGroupActive(loan);
|
checkClientOrGroupActive(loan);
|
||||||
|
|
||||||
|
final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling();
|
||||||
|
final Date rescheduledRepaymentDate = command.DateValueOfParameterNamed("adjustRepaymentDate");
|
||||||
|
|
||||||
// check for product mix validations
|
// check for product mix validations
|
||||||
checkForProductMixRestrictions(loan);
|
checkForProductMixRestrictions(loan);
|
||||||
|
|
||||||
@@ -333,7 +337,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
LocalDate recalculateFrom = null;
|
LocalDate recalculateFrom = null;
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||||
|
|
||||||
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, rescheduledRepaymentDate);
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
|
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
|
||||||
loan, null);
|
loan, null);
|
||||||
@@ -519,6 +523,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
if (disbursalCommand == null) { return changes; }
|
if (disbursalCommand == null) { return changes; }
|
||||||
|
|
||||||
|
final LocalDate nextPossibleRepaymentDate = null;
|
||||||
|
final Date rescheduledRepaymentDate = null;
|
||||||
|
|
||||||
for (int i = 0; i < disbursalCommand.length; i++) {
|
for (int i = 0; i < disbursalCommand.length; i++) {
|
||||||
final SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i];
|
final SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i];
|
||||||
|
|
||||||
@@ -563,7 +570,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
LocalDate recalculateFrom = null;
|
LocalDate recalculateFrom = null;
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||||
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, rescheduledRepaymentDate);
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
|
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
|
||||||
loan.fetchRepaymentScheduleInstallments(), loan, null);
|
loan.fetchRepaymentScheduleInstallments(), loan, null);
|
||||||
@@ -2669,11 +2676,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final boolean recalculateSchedule,
|
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final boolean recalculateSchedule,
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate nextPossibleRepaymentDate, final Date rescheduledRepaymentDate) {
|
||||||
AppUser currentUser = getAppUserIfPresent();
|
AppUser currentUser = getAppUserIfPresent();
|
||||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||||
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount, currentUser);
|
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount, currentUser,
|
||||||
|
nextPossibleRepaymentDate, rescheduledRepaymentDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {
|
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {
|
||||||
|
|||||||
Reference in New Issue
Block a user