MIFOSX-1584 Added business logic for minimum number of days between disbursal day and first repayment date
This commit is contained in:
+201
@@ -0,0 +1,201 @@
|
|||||||
|
/**
|
||||||
|
* 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 static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mifosplatform.integrationtests.common.CalendarHelper;
|
||||||
|
import org.mifosplatform.integrationtests.common.ClientHelper;
|
||||||
|
import org.mifosplatform.integrationtests.common.CommonConstants;
|
||||||
|
import org.mifosplatform.integrationtests.common.GroupHelper;
|
||||||
|
import org.mifosplatform.integrationtests.common.Utils;
|
||||||
|
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 org.mifosplatform.integrationtests.common.savings.SavingsAccountHelper;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the creation, approval and rejection of a loan reschedule request
|
||||||
|
**/
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
public class MinimumDaysBetweenDisbursalAndFirstRepaymentTest {
|
||||||
|
|
||||||
|
private ResponseSpecification responseSpec;
|
||||||
|
private ResponseSpecification responseSpecForStatusCode403;
|
||||||
|
private RequestSpecification requestSpec;
|
||||||
|
private LoanTransactionHelper loanTransactionHelper;
|
||||||
|
private Integer clientId;
|
||||||
|
private Integer groupId;
|
||||||
|
private Integer groupCalendarId;
|
||||||
|
private Integer loanProductId;
|
||||||
|
private Integer loanId;
|
||||||
|
private String loanPrincipalAmount = "100000.00";
|
||||||
|
private String numberOfRepayments = "12";
|
||||||
|
private String interestRatePerPeriod = "18";
|
||||||
|
private String groupActivationDate = "1 August 2014";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
Utils.initializeRESTAssured();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MinimumDaysBetweenDisbursalAndFirstRepayment is set to 7 days and days
|
||||||
|
* between disbursal date and first repayment is set as 7. system should
|
||||||
|
* allow to create this loan and allow to disburse
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void createLoanEntity_WITH_DAY_BETWEEN_DISB_DATE_AND_REPAY_START_DATE_GREATER_THAN_MIN_DAY_CRITERIA() {
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// create all required entities
|
||||||
|
this.createRequiredEntities();
|
||||||
|
|
||||||
|
final String disbursalDate = "4 September 2014";
|
||||||
|
final String firstRepaymentDate = "11 September 2014";
|
||||||
|
|
||||||
|
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(loanPrincipalAmount)
|
||||||
|
.withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsWeeks().withNumberOfRepayments(numberOfRepayments)
|
||||||
|
.withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments()
|
||||||
|
.withInterestCalculationPeriodTypeAsDays().withInterestRatePerPeriod(interestRatePerPeriod)
|
||||||
|
.withRepaymentFrequencyTypeAsWeeks().withSubmittedOnDate(disbursalDate).withExpectedDisbursementDate(disbursalDate)
|
||||||
|
.withPrincipalGrace("2").withInterestGrace("2").withFirstRepaymentDate(firstRepaymentDate)
|
||||||
|
.build(this.clientId.toString(), this.loanProductId.toString(), null);
|
||||||
|
|
||||||
|
this.loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||||
|
|
||||||
|
// Test for loan account is created
|
||||||
|
Assert.assertNotNull(this.loanId);
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, this.loanId);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
// Test for loan account is created, can be approved
|
||||||
|
this.loanTransactionHelper.approveLoan(disbursalDate, this.loanId);
|
||||||
|
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, this.loanId);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
|
||||||
|
// Test for loan account approved can be disbursed
|
||||||
|
this.loanTransactionHelper.disburseLoan(disbursalDate, this.loanId);
|
||||||
|
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, this.loanId);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MinimumDaysBetweenDisbursalAndFirstRepayment is set to 7 days and days
|
||||||
|
* between disbursal date and first repayment is set as 7. system should
|
||||||
|
* allow to create this loan and allow to disburse
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void createLoanEntity_WITH_DAY_BETWEEN_DISB_DATE_AND_REPAY_START_DATE_LESS_THAN_MIN_DAY_CRITERIA() {
|
||||||
|
|
||||||
|
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||||
|
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||||
|
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||||
|
this.responseSpecForStatusCode403 = new ResponseSpecBuilder().expectStatusCode(403).build();
|
||||||
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
|
// create all required entities
|
||||||
|
this.createRequiredEntities();
|
||||||
|
|
||||||
|
// loanTransactionHelper is reassigned to accept 403 status code from
|
||||||
|
// server
|
||||||
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpecForStatusCode403);
|
||||||
|
|
||||||
|
final String disbursalDate = "4 September 2014";
|
||||||
|
final String firstRepaymentDate = "5 September 2014";
|
||||||
|
|
||||||
|
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(loanPrincipalAmount)
|
||||||
|
.withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsWeeks().withNumberOfRepayments(numberOfRepayments)
|
||||||
|
.withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments()
|
||||||
|
.withInterestCalculationPeriodTypeAsDays().withInterestRatePerPeriod(interestRatePerPeriod)
|
||||||
|
.withRepaymentFrequencyTypeAsWeeks().withSubmittedOnDate(disbursalDate).withExpectedDisbursementDate(disbursalDate)
|
||||||
|
.withPrincipalGrace("2").withInterestGrace("2").withFirstRepaymentDate(firstRepaymentDate)
|
||||||
|
.build(this.clientId.toString(), this.loanProductId.toString(), null);
|
||||||
|
|
||||||
|
List<HashMap> error = (List<HashMap>) this.loanTransactionHelper.createLoanAccount(loanApplicationJSON,
|
||||||
|
CommonConstants.RESPONSE_ERROR);
|
||||||
|
assertEquals("validation.msg.loan.days.between.first.repayment.and.disbursal.are.less than.minimum.number.of.days.required", error
|
||||||
|
.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the client, loan product, and loan entities
|
||||||
|
**/
|
||||||
|
private void createRequiredEntities() {
|
||||||
|
final String minimumDaysBetweenDisbursalAndFirstRepayment = "7"; // &
|
||||||
|
// days
|
||||||
|
this.createGroupEntityWithCalendar();
|
||||||
|
this.createClientEntity();
|
||||||
|
this.associateClientToGroup(this.groupId, this.clientId);
|
||||||
|
this.createLoanProductEntity(minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Associate client to the group
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void associateClientToGroup(final Integer groupId, final Integer clientId) {
|
||||||
|
GroupHelper.associateClient(this.requestSpec, this.responseSpec, groupId.toString(), clientId.toString());
|
||||||
|
GroupHelper.verifyGroupMembers(this.requestSpec, this.responseSpec, groupId, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new group
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void createGroupEntityWithCalendar() {
|
||||||
|
this.groupId = GroupHelper.createGroup(this.requestSpec, this.responseSpec, this.groupActivationDate);
|
||||||
|
GroupHelper.verifyGroupCreatedOnServer(this.requestSpec, this.responseSpec, this.groupId);
|
||||||
|
|
||||||
|
final String startDate = this.groupActivationDate;
|
||||||
|
final String frequency = "2"; // 2:Weekly
|
||||||
|
final String interval = "1"; // Every one week
|
||||||
|
final String repeatsOnDay = "1"; // 1:Monday
|
||||||
|
|
||||||
|
this.groupCalendarId = CalendarHelper.createMeetingCalendarForGroup(this.requestSpec, this.responseSpec, this.groupId, startDate,
|
||||||
|
frequency, interval, repeatsOnDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new client
|
||||||
|
**/
|
||||||
|
private void createClientEntity() {
|
||||||
|
this.clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, this.clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a new loan product
|
||||||
|
**/
|
||||||
|
private void createLoanProductEntity(final String minimumDaysBetweenDisbursalAndFirstRepayment) {
|
||||||
|
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(loanPrincipalAmount)
|
||||||
|
.withNumberOfRepayments(numberOfRepayments).withinterestRatePerPeriod(interestRatePerPeriod)
|
||||||
|
.withInterestRateFrequencyTypeAsYear()
|
||||||
|
.withMinimumDaysBetweenDisbursalAndFirstRepayment(minimumDaysBetweenDisbursalAndFirstRepayment).build(null);
|
||||||
|
this.loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
import static com.jayway.restassured.path.json.JsonPath.from;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.jayway.restassured.specification.RequestSpecification;
|
||||||
|
import com.jayway.restassured.specification.ResponseSpecification;
|
||||||
|
|
||||||
|
public class CalendarHelper {
|
||||||
|
|
||||||
|
private static final String BASE_URL = "/mifosng-provider/api/v1/";
|
||||||
|
private static final String PARENT_ENTITY_NAME = "groups/";
|
||||||
|
private static final String ENITY_NAME = "/calendars";
|
||||||
|
|
||||||
|
public static Integer createMeetingCalendarForGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||||
|
final Integer groupId, final String startDate, final String frequency, final String interval, final String repeatsOnDay) {
|
||||||
|
|
||||||
|
System.out.println("---------------------------------CREATING A MEETING CALENDAR FOR THE GROUP------------------------------");
|
||||||
|
|
||||||
|
final String CALENDAR_RESOURCE_URL = BASE_URL + PARENT_ENTITY_NAME + groupId + ENITY_NAME + "?" + Utils.TENANT_IDENTIFIER;
|
||||||
|
|
||||||
|
System.out.println(CALENDAR_RESOURCE_URL);
|
||||||
|
|
||||||
|
return Utils.performServerPost(requestSpec, responseSpec, CALENDAR_RESOURCE_URL, getTestCalendarAsJSON(frequency, interval, repeatsOnDay, startDate),
|
||||||
|
"resourceId");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTestCalendarAsJSON(final String frequency, final String interval,final String repeatsOnDay,
|
||||||
|
final String startDate) {
|
||||||
|
|
||||||
|
final HashMap<String, String> map = new HashMap<>();
|
||||||
|
map.put("dateFormat", "dd MMMM yyyy");
|
||||||
|
map.put("locale", "en");
|
||||||
|
map.put("frequency", frequency);
|
||||||
|
map.put("interval", interval);
|
||||||
|
map.put("repeating", "true");
|
||||||
|
map.put("repeatsOnDay", repeatsOnDay );
|
||||||
|
map.put("title", Utils.randomNameGenerator("groups_CollectionMeeting", 4));
|
||||||
|
map.put("typeId", "1");
|
||||||
|
map.put("startDate", startDate);
|
||||||
|
System.out.println("map : " + map);
|
||||||
|
return new Gson().toJson(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void verifyCalendarCreatedOnServer(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||||
|
final Integer generatedGroupId, final Integer generatedCalendarId) {
|
||||||
|
System.out.println("------------------------------CHECK CALENDAR DETAILS------------------------------------\n");
|
||||||
|
final String CLIENT_URL = "/mifosng-provider/api/v1/groups/" + generatedGroupId + "?associations=all&" + Utils.TENANT_IDENTIFIER;
|
||||||
|
final String responseCalendarDetailsinJSON = Utils.performServerGet(requestSpec, responseSpec, CLIENT_URL, "collectionMeetingCalendar");
|
||||||
|
final Integer responseCalendarId = from(responseCalendarDetailsinJSON).get("id");
|
||||||
|
assertEquals("ERROR IN CREATING THE CALENDAR", generatedCalendarId, responseCalendarId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -47,6 +47,7 @@ public class LoanApplicationTestBuilder {
|
|||||||
private List<HashMap> disbursementData = null;
|
private List<HashMap> disbursementData = null;
|
||||||
private List<HashMap> charges = new ArrayList<HashMap>();
|
private List<HashMap> charges = new ArrayList<HashMap>();
|
||||||
private String recalculationRestFrequencyDate = null;
|
private String recalculationRestFrequencyDate = null;
|
||||||
|
private String repaymentsStartingFromDate = null;
|
||||||
|
|
||||||
public String build(final String ID, final String loanProductId, final String savingsID) {
|
public String build(final String ID, final String loanProductId, final String savingsID) {
|
||||||
|
|
||||||
@@ -73,6 +74,8 @@ public class LoanApplicationTestBuilder {
|
|||||||
map.put("expectedDisbursementDate", this.expectedDisbursmentDate);
|
map.put("expectedDisbursementDate", this.expectedDisbursmentDate);
|
||||||
map.put("submittedOnDate", this.submittedOnDate);
|
map.put("submittedOnDate", this.submittedOnDate);
|
||||||
map.put("loanType", this.loanType);
|
map.put("loanType", this.loanType);
|
||||||
|
map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate);
|
||||||
|
|
||||||
if (charges != null) {
|
if (charges != null) {
|
||||||
map.put("charges", charges);
|
map.put("charges", charges);
|
||||||
}
|
}
|
||||||
@@ -240,4 +243,9 @@ public class LoanApplicationTestBuilder {
|
|||||||
this.recalculationRestFrequencyDate = recalculationRestFrequencyDate;
|
this.recalculationRestFrequencyDate = recalculationRestFrequencyDate;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanApplicationTestBuilder withFirstRepaymentDate(final String firstRepaymentDate) {
|
||||||
|
this.repaymentsStartingFromDate = firstRepaymentDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -87,6 +87,7 @@ public class LoanProductTestBuilder {
|
|||||||
private String recalculationRestFrequencyType = "1";
|
private String recalculationRestFrequencyType = "1";
|
||||||
private String recalculationRestFrequencyInterval = "0";
|
private String recalculationRestFrequencyInterval = "0";
|
||||||
private String recalculationRestFrequencyDate = null;
|
private String recalculationRestFrequencyDate = null;
|
||||||
|
private String minimumDaysBetweenDisbursalAndFirstRepayment = "0";
|
||||||
|
|
||||||
public String build(final String chargeId) {
|
public String build(final String chargeId) {
|
||||||
final HashMap<String, Object> map = new HashMap<>();
|
final HashMap<String, Object> map = new HashMap<>();
|
||||||
@@ -120,6 +121,7 @@ public class LoanProductTestBuilder {
|
|||||||
map.put("minPrincipal", this.minPrincipal);
|
map.put("minPrincipal", this.minPrincipal);
|
||||||
map.put("maxPrincipal", this.maxPrincipal);
|
map.put("maxPrincipal", this.maxPrincipal);
|
||||||
map.put("overdueDaysForNPA", this.overdueDaysForNPA);
|
map.put("overdueDaysForNPA", this.overdueDaysForNPA);
|
||||||
|
map.put("minimumDaysBetweenDisbursalAndFirstRepayment", this.minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
if (multiDisburseLoan) {
|
if (multiDisburseLoan) {
|
||||||
map.put("multiDisburseLoan", this.multiDisburseLoan);
|
map.put("multiDisburseLoan", this.multiDisburseLoan);
|
||||||
map.put("maxTrancheCount", this.maxTrancheCount);
|
map.put("maxTrancheCount", this.maxTrancheCount);
|
||||||
@@ -377,4 +379,8 @@ public class LoanProductTestBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanProductTestBuilder withMinimumDaysBetweenDisbursalAndFirstRepayment(final String minimumDaysBetweenDisbursalAndFirstRepayment){
|
||||||
|
this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -49,6 +49,10 @@ public class LoanTransactionHelper {
|
|||||||
return Utils.performServerPost(this.requestSpec, this.responseSpec, APPLY_LOAN_URL, loanApplicationJSON, "loanId");
|
return Utils.performServerPost(this.requestSpec, this.responseSpec, APPLY_LOAN_URL, loanApplicationJSON, "loanId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object createLoanAccount(final String loanApplicationJSON, final String responseAttribute) {
|
||||||
|
return Utils.performServerPost(this.requestSpec, this.responseSpec, APPLY_LOAN_URL, loanApplicationJSON, responseAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
public Integer updateLoan(final Integer id, final String loanApplicationJSON) {
|
public Integer updateLoan(final Integer id, final String loanApplicationJSON) {
|
||||||
return Utils.performServerPut(this.requestSpec, this.responseSpec, "/mifosng-provider/api/v1/loans/" + id
|
return Utils.performServerPut(this.requestSpec, this.responseSpec, "/mifosng-provider/api/v1/loans/" + id
|
||||||
+ "?" + Utils.TENANT_IDENTIFIER, loanApplicationJSON, "loanId");
|
+ "?" + Utils.TENANT_IDENTIFIER, loanApplicationJSON, "loanId");
|
||||||
|
|||||||
+4
@@ -130,6 +130,10 @@ public class DataValidatorBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*** FIXME: Vishwas, why does this method have a parameter? Seems wrong ***/
|
/*** FIXME: Vishwas, why does this method have a parameter? Seems wrong ***/
|
||||||
|
/*
|
||||||
|
* This method is not meant for validation, if you have mandatory boolean param and
|
||||||
|
* if it has invalid value or value not passed then call this method, this method is always used with input as false
|
||||||
|
*/
|
||||||
public DataValidatorBuilder trueOrFalseRequired(final boolean trueOfFalseFieldProvided) {
|
public DataValidatorBuilder trueOrFalseRequired(final boolean trueOfFalseFieldProvided) {
|
||||||
if (!trueOfFalseFieldProvided && !this.ignoreNullValue) {
|
if (!trueOfFalseFieldProvided && !this.ignoreNullValue) {
|
||||||
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
|
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
|
||||||
|
|||||||
+9
-1
@@ -58,7 +58,15 @@ public enum PeriodFrequencyType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isYearly() {
|
public boolean isYearly() {
|
||||||
return this.value.equals(PeriodFrequencyType.MONTHS.getValue());
|
return this.value.equals(PeriodFrequencyType.YEARS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWeekly() {
|
||||||
|
return this.value.equals(PeriodFrequencyType.WEEKS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDaily() {
|
||||||
|
return this.value.equals(PeriodFrequencyType.DAYS.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInvalid() {
|
public boolean isInvalid() {
|
||||||
|
|||||||
+3
-3
@@ -9,9 +9,9 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRul
|
|||||||
|
|
||||||
public class LoanDisbursalException extends AbstractPlatformDomainRuleException {
|
public class LoanDisbursalException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
public LoanDisbursalException(final String cureentProduct, final String restrictedProduct) {
|
public LoanDisbursalException(final String currentProduct, final String restrictedProduct) {
|
||||||
super("error.msg.loan.disbursal.failed", "This loan could not be disbursed as `" + cureentProduct + "` and `" + restrictedProduct
|
super("error.msg.loan.disbursal.failed", "This loan could not be disbursed as `" + currentProduct + "` and `" + restrictedProduct
|
||||||
+ "` are not allowed to co-exist", new Object[] { cureentProduct, restrictedProduct });
|
+ "` are not allowed to co-exist", new Object[] { currentProduct, restrictedProduct });
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanDisbursalException(final String defaultUserMessage, final String entity, final Object... defaultUserMessageArgs) {
|
public LoanDisbursalException(final String defaultUserMessage, final String entity, final Object... defaultUserMessageArgs) {
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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.portfolio.loanaccount.exception;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AbstractPlatformDomainRuleException} thrown when a number of days between disbursal date and firstRepayment is less than
|
||||||
|
* minimumDaysBetweenDisbursalAndFirstRepayment
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException(final String defaultUserMessage,
|
||||||
|
final Object... defaultUserMessageArgs) {
|
||||||
|
super("validation.msg.loan.days.between.first.repayment.and.disbursal.are.less than.minimum.number.of.days.required",
|
||||||
|
defaultUserMessage, defaultUserMessageArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
+144
-27
@@ -28,6 +28,7 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
|||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||||
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||||
|
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||||
import org.mifosplatform.portfolio.calendar.domain.CalendarRepository;
|
import org.mifosplatform.portfolio.calendar.domain.CalendarRepository;
|
||||||
@@ -50,6 +51,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateException;
|
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateException;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.exception.MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||||
@@ -160,44 +162,97 @@ public class LoanScheduleAssembler {
|
|||||||
|
|
||||||
final LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
|
final LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
|
||||||
final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
|
final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
|
||||||
LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
|
LocalDate calculatedRepaymentsStartingFromDate = null;
|
||||||
|
|
||||||
final Boolean synchDisbursement = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
|
final Boolean synchDisbursement = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
|
||||||
final Long calendarId = this.fromApiJsonHelper.extractLongNamed("calendarId", element);
|
final Long calendarId = this.fromApiJsonHelper.extractLongNamed("calendarId", element);
|
||||||
Calendar calendar = null;
|
Calendar calendar = null;
|
||||||
if ((synchDisbursement != null && synchDisbursement.booleanValue()) || (calendarId != null && calendarId != 0)) {
|
|
||||||
calendar = this.calendarRepository.findOne(calendarId);
|
final String loanTypeParameterName = "loanType";
|
||||||
if (calendar == null) { throw new CalendarNotFoundException(calendarId); }
|
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
|
||||||
|
|
||||||
|
final AccountType loanType = AccountType.fromName(loanTypeStr);
|
||||||
|
|
||||||
|
|
||||||
// validate repayment frequency and interval with meeting frequency
|
/*
|
||||||
// and interval
|
* If it is JLG loan then make sure loan frequency is same as
|
||||||
final PeriodFrequencyType meetingPeriodFrequency = CalendarUtils.getMeetingPeriodFrequencyType(calendar.getRecurrence());
|
* Group/Center meeting frequency or multiple of it. TODO: Check should
|
||||||
validateRepaymentFrequencyIsSameAsMeetingFrequency(meetingPeriodFrequency.getValue(), repaymentFrequencyType,
|
* be either same frequency or loan freq is multiple of center/group
|
||||||
CalendarUtils.getInterval(calendar.getRecurrence()), repaymentEvery);
|
* meeting freq multiples
|
||||||
|
*/
|
||||||
|
if (loanType.isJLGAccount()) {
|
||||||
|
if ((synchDisbursement != null && synchDisbursement.booleanValue())) {
|
||||||
|
calendar = this.calendarRepository.findOne(calendarId);
|
||||||
|
if (calendar == null) { throw new CalendarNotFoundException(calendarId); }
|
||||||
|
final PeriodFrequencyType meetingPeriodFrequency = CalendarUtils.getMeetingPeriodFrequencyType(calendar.getRecurrence());
|
||||||
|
validateRepaymentFrequencyIsSameAsMeetingFrequency(meetingPeriodFrequency.getValue(), repaymentFrequencyType,
|
||||||
|
CalendarUtils.getInterval(calendar.getRecurrence()), repaymentEvery);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (synchDisbursement != null && synchDisbursement.booleanValue()) {
|
/*
|
||||||
validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar);
|
* If user has not passed the first repayments date then only system
|
||||||
|
* will derive it based on loan is individual/group loan or JLG loan.
|
||||||
|
*/
|
||||||
|
Boolean isRepaymentsStartingFromDateDerived = false;
|
||||||
|
if (repaymentsStartingFromDate == null) {
|
||||||
|
calculatedRepaymentsStartingFromDate = deriveFirstRepaymentDate(loanType, repaymentEvery, expectedDisbursementDate,
|
||||||
|
repaymentPeriodFrequencyType, loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment(), calendar);
|
||||||
|
isRepaymentsStartingFromDateDerived = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if calendar is attached (not null) then reschedule repayment dates
|
/*
|
||||||
// according to meeting
|
* If it is JLG loan, then make sure first repayment falls on meeting
|
||||||
if (null != calendar) {
|
* date
|
||||||
// TODO : AA - Is it require to reset repaymentsStartingFromDate or
|
*/
|
||||||
// set only if it is not provided (or null)
|
if (loanType.isJLGAccount()) {
|
||||||
// Currently provided repaymentsStartingFromDate takes precedence
|
if (isRepaymentsStartingFromDateDerived) {
|
||||||
// over system generated next meeting date
|
validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar);
|
||||||
if (calculatedRepaymentsStartingFromDate == null) {
|
} else {
|
||||||
// FIXME: AA - Possibility of having next meeting date
|
|
||||||
// immediately after disbursement date,
|
|
||||||
// need to have minimum number of days gap between disbursement
|
|
||||||
// and first repayment date.
|
|
||||||
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
|
|
||||||
calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar, expectedDisbursementDate,
|
|
||||||
repaymentEvery, frequency);
|
|
||||||
} else {// validate user provided repaymentsStartFromDate
|
|
||||||
validateRepaymentsStartDateWithMeetingDates(repaymentsStartingFromDate, calendar);
|
validateRepaymentsStartDateWithMeetingDates(repaymentsStartingFromDate, calendar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it is JLG loan and disbursement is synced on meeting, make sure
|
||||||
|
* disbursement date on meeting date
|
||||||
|
*/
|
||||||
|
if (loanType.isJLGAccount()) {
|
||||||
|
if (synchDisbursement != null && synchDisbursement.booleanValue()) {
|
||||||
|
validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If minimum days between disbursal and first repayment is set, then
|
||||||
|
* validate the rule
|
||||||
|
*/
|
||||||
|
if (isRepaymentsStartingFromDateDerived) {
|
||||||
|
validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate, calculatedRepaymentsStartingFromDate,
|
||||||
|
loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment());
|
||||||
|
} else {
|
||||||
|
validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate, repaymentsStartingFromDate,
|
||||||
|
loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If first repayment is not derived and instead directly taken from
|
||||||
|
* input then copy the value of repaymentsStartingFromDate to
|
||||||
|
* calculatedRepaymentsStartingFromDate
|
||||||
|
*
|
||||||
|
* TODO: The reason for this bad code is, LoanApplicationTerms.assembleFrom
|
||||||
|
* takes both repaymentsStartingFromDate and calculatedRepaymentsStartingFromDate
|
||||||
|
* and derived data has precedence over input date,
|
||||||
|
*
|
||||||
|
* FIXME:LoanApplicationTerms.assembleFrom should take only one
|
||||||
|
* "first repayment date" than taking two dates with same purpose.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (!isRepaymentsStartingFromDateDerived) {
|
||||||
|
calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
|
||||||
|
}
|
||||||
|
|
||||||
// grace details
|
// grace details
|
||||||
final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
|
final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
|
||||||
final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
|
final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
|
||||||
@@ -424,4 +479,66 @@ public class LoanScheduleAssembler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LocalDate deriveFirstRepaymentDate(final AccountType loanType, final Integer repaymentEvery,
|
||||||
|
final LocalDate expectedDisbursementDate, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar){
|
||||||
|
|
||||||
|
LocalDate derivedFirstRepayment = null;
|
||||||
|
LocalDate dateBasedOnRepaymentFrequency = null;
|
||||||
|
final LocalDate dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
|
|
||||||
|
if (loanType.isIndividualAccount() || loanType.isGroupAccount()) {
|
||||||
|
// Derive the first repayment date as greater date among (disbursement date + plus frequency) or
|
||||||
|
// (disbursement date + minimum between disbursal and first repayment )
|
||||||
|
if (repaymentPeriodFrequencyType.isDaily()) {
|
||||||
|
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusDays(repaymentEvery);
|
||||||
|
}
|
||||||
|
else if(repaymentPeriodFrequencyType.isWeekly()){
|
||||||
|
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery);
|
||||||
|
}
|
||||||
|
else if (repaymentPeriodFrequencyType.isMonthly()) {
|
||||||
|
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery);
|
||||||
|
}
|
||||||
|
else if (repaymentPeriodFrequencyType.isYearly()){
|
||||||
|
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery);
|
||||||
|
}
|
||||||
|
derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment)?dateBasedOnRepaymentFrequency:dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
}
|
||||||
|
else if(loanType.isJLGAccount()){
|
||||||
|
|
||||||
|
final LocalDate refernceDateForCalculatingFirstRepaymentDate = expectedDisbursementDate;
|
||||||
|
derivedFirstRepayment = deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate,refernceDateForCalculatingFirstRepaymentDate,
|
||||||
|
repaymentPeriodFrequencyType,minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return derivedFirstRepayment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDate deriveFirstRepaymentDateForJLGLoans(final Integer repaymentEvery,
|
||||||
|
final LocalDate expectedDisbursementDate, final LocalDate refernceDateForCalculatingFirstRepaymentDate,
|
||||||
|
final PeriodFrequencyType repaymentPeriodFrequencyType,final Integer minimumDaysBetweenDisbursalAndFirstRepayment,
|
||||||
|
final Calendar calendar){
|
||||||
|
|
||||||
|
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
|
||||||
|
final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar, refernceDateForCalculatingFirstRepaymentDate,
|
||||||
|
repaymentEvery, frequency);
|
||||||
|
final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
|
return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment
|
||||||
|
:deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate,derivedFirstRepayment, repaymentPeriodFrequencyType,
|
||||||
|
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateMinimumDaysBetweenDisbursalAndFirstRepayment(final LocalDate disbursalDate,
|
||||||
|
final LocalDate firstRepaymentDate, final Integer minimumDaysBetweenDisbursalAndFirstRepayment){
|
||||||
|
|
||||||
|
final LocalDate minimumFirstRepaymentDate = disbursalDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
|
if(firstRepaymentDate.isBefore(minimumFirstRepaymentDate)){
|
||||||
|
final String errorMessage = "Number of days between disbursal date (" + disbursalDate + ") and first repayment date ("
|
||||||
|
+ firstRepaymentDate + ") can't be less than minimumDaysBetweenDisbursalAndFirstRepayment ("
|
||||||
|
+ minimumDaysBetweenDisbursalAndFirstRepayment + ")." ;
|
||||||
|
throw new MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+13
-19
@@ -111,6 +111,19 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
if (loanType.isJLGAccount()) {
|
if (loanType.isJLGAccount()) {
|
||||||
baseDataValidator.reset().parameter("clientId").value(clientId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter("clientId").value(clientId).notNull().integerGreaterThanZero();
|
||||||
baseDataValidator.reset().parameter("groupId").value(groupId).notNull().longGreaterThanZero();
|
baseDataValidator.reset().parameter("groupId").value(groupId).notNull().longGreaterThanZero();
|
||||||
|
|
||||||
|
// if it is JLG loan then must have a meeting details (calendar)
|
||||||
|
final String calendarIdParameterName = "calendarId";
|
||||||
|
final Long calendarId = this.fromApiJsonHelper.extractLongNamed(calendarIdParameterName, element);
|
||||||
|
baseDataValidator.reset().parameter(calendarIdParameterName).value(calendarId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
// if it is JLG loan then must have a syncDisbursement
|
||||||
|
final String syncDisbursementParameterName = "syncDisbursementWithMeeting";
|
||||||
|
final Boolean syncDisbursement = this.fromApiJsonHelper.extractBooleanNamed(syncDisbursementParameterName, element);
|
||||||
|
|
||||||
|
if (syncDisbursement == null) {
|
||||||
|
baseDataValidator.reset().parameter(syncDisbursementParameterName).value(syncDisbursement).trueOrFalseRequired(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -331,25 +344,6 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean meetingIdRequired = false;
|
|
||||||
// validate syncDisbursement
|
|
||||||
final String syncDisbursementParameterName = "syncDisbursementWithMeeting";
|
|
||||||
if (this.fromApiJsonHelper.parameterExists(syncDisbursementParameterName, element)) {
|
|
||||||
final Boolean syncDisbursement = this.fromApiJsonHelper.extractBooleanNamed(syncDisbursementParameterName, element);
|
|
||||||
if (syncDisbursement == null) {
|
|
||||||
baseDataValidator.reset().parameter(syncDisbursementParameterName).value(syncDisbursement).trueOrFalseRequired(false);
|
|
||||||
} else if (syncDisbursement.booleanValue()) {
|
|
||||||
meetingIdRequired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final String calendarIdParameterName = "calendarId";
|
|
||||||
// if disbursement is synced then must have a meeting (calendar)
|
|
||||||
if (meetingIdRequired || this.fromApiJsonHelper.parameterExists(calendarIdParameterName, element)) {
|
|
||||||
final Long calendarId = this.fromApiJsonHelper.extractLongNamed(calendarIdParameterName, element);
|
|
||||||
baseDataValidator.reset().parameter(calendarIdParameterName).value(calendarId).notNull().integerGreaterThanZero();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
|
||||||
final BigDecimal emiAnount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
final BigDecimal emiAnount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
||||||
element);
|
element);
|
||||||
|
|||||||
+1
-1
@@ -59,8 +59,8 @@ public interface LoanProductConstants {
|
|||||||
public static final String outstandingLoanBalanceParameterName = "outstandingLoanBalance";
|
public static final String outstandingLoanBalanceParameterName = "outstandingLoanBalance";
|
||||||
|
|
||||||
public static final String graceOnArrearsAgeingParameterName = "graceOnArrearsAgeing";
|
public static final String graceOnArrearsAgeingParameterName = "graceOnArrearsAgeing";
|
||||||
|
|
||||||
public static final String overdueDaysForNPAParameterName = "overdueDaysForNPA";
|
public static final String overdueDaysForNPAParameterName = "overdueDaysForNPA";
|
||||||
|
public static final String minimumDaysBetweenDisbursalAndFirstRepayment = "minimumDaysBetweenDisbursalAndFirstRepayment";
|
||||||
|
|
||||||
// Interest recalculation related
|
// Interest recalculation related
|
||||||
public static final String isInterestRecalculationEnabledParameterName = "isInterestRecalculationEnabled";
|
public static final String isInterestRecalculationEnabledParameterName = "isInterestRecalculationEnabled";
|
||||||
|
|||||||
+12
-4
@@ -83,6 +83,7 @@ public class LoanProductData {
|
|||||||
private final EnumOptionData daysInYearType;
|
private final EnumOptionData daysInYearType;
|
||||||
private final boolean isInterestRecalculationEnabled;
|
private final boolean isInterestRecalculationEnabled;
|
||||||
private final LoanProductInterestRecalculationData interestRecalculationData;
|
private final LoanProductInterestRecalculationData interestRecalculationData;
|
||||||
|
private final Integer minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
|
||||||
// charges
|
// charges
|
||||||
private final Collection<ChargeData> charges;
|
private final Collection<ChargeData> charges;
|
||||||
@@ -186,6 +187,7 @@ public class LoanProductData {
|
|||||||
final EnumOptionData daysInYearType = null;
|
final EnumOptionData daysInYearType = null;
|
||||||
final boolean isInterestRecalculationEnabled = false;
|
final boolean isInterestRecalculationEnabled = false;
|
||||||
final LoanProductInterestRecalculationData interestRecalculationData = null;
|
final LoanProductInterestRecalculationData interestRecalculationData = null;
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
||||||
|
|
||||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||||
@@ -195,7 +197,7 @@ public class LoanProductData {
|
|||||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
||||||
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
||||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
interestRecalculationData);
|
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
|
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
|
||||||
@@ -248,6 +250,7 @@ public class LoanProductData {
|
|||||||
final EnumOptionData daysInYearType = null;
|
final EnumOptionData daysInYearType = null;
|
||||||
final boolean isInterestRecalculationEnabled = false;
|
final boolean isInterestRecalculationEnabled = false;
|
||||||
final LoanProductInterestRecalculationData interestRecalculationData = null;
|
final LoanProductInterestRecalculationData interestRecalculationData = null;
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
||||||
|
|
||||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||||
@@ -257,7 +260,7 @@ public class LoanProductData {
|
|||||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
||||||
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
||||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
interestRecalculationData);
|
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
|
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
|
||||||
@@ -317,6 +320,8 @@ public class LoanProductData {
|
|||||||
final boolean isInterestRecalculationEnabled = false;
|
final boolean isInterestRecalculationEnabled = false;
|
||||||
final LoanProductInterestRecalculationData interestRecalculationData = LoanProductInterestRecalculationData
|
final LoanProductInterestRecalculationData interestRecalculationData = LoanProductInterestRecalculationData
|
||||||
.sensibleDefaultsForNewLoanProductCreation();
|
.sensibleDefaultsForNewLoanProductCreation();
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
||||||
|
|
||||||
|
|
||||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||||
@@ -326,7 +331,7 @@ public class LoanProductData {
|
|||||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId,
|
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId,
|
||||||
principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle,
|
principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle,
|
||||||
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType,
|
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType,
|
||||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData);
|
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
||||||
@@ -356,7 +361,8 @@ public class LoanProductData {
|
|||||||
Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations, Boolean multiDisburseLoan,
|
Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations, Boolean multiDisburseLoan,
|
||||||
Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
|
Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
|
||||||
final Integer overdueDaysForNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType,
|
final Integer overdueDaysForNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType,
|
||||||
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData) {
|
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData,
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
@@ -394,6 +400,7 @@ public class LoanProductData {
|
|||||||
this.closeDate = closeDate;
|
this.closeDate = closeDate;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
|
this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
|
||||||
this.chargeOptions = null;
|
this.chargeOptions = null;
|
||||||
this.penaltyOptions = null;
|
this.penaltyOptions = null;
|
||||||
@@ -508,6 +515,7 @@ public class LoanProductData {
|
|||||||
this.multiDisburseLoan = productData.multiDisburseLoan;
|
this.multiDisburseLoan = productData.multiDisburseLoan;
|
||||||
this.maxTrancheCount = productData.maxTrancheCount;
|
this.maxTrancheCount = productData.maxTrancheCount;
|
||||||
this.outstandingLoanBalance = productData.outstandingLoanBalance;
|
this.outstandingLoanBalance = productData.outstandingLoanBalance;
|
||||||
|
this.minimumDaysBetweenDisbursalAndFirstRepayment = productData.minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
|
||||||
this.amortizationTypeOptions = amortizationTypeOptions;
|
this.amortizationTypeOptions = amortizationTypeOptions;
|
||||||
this.interestTypeOptions = interestTypeOptions;
|
this.interestTypeOptions = interestTypeOptions;
|
||||||
|
|||||||
+22
-2
@@ -121,6 +121,9 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
@Column(name = "overdue_days_for_npa", nullable = true)
|
@Column(name = "overdue_days_for_npa", nullable = true)
|
||||||
private Integer overdueDaysForNPA;
|
private Integer overdueDaysForNPA;
|
||||||
|
|
||||||
|
@Column(name = "min_days_between_disbursal_and_first_repayment", nullable = true)
|
||||||
|
private Integer minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
|
||||||
@LazyCollection(LazyCollectionOption.FALSE)
|
@LazyCollection(LazyCollectionOption.FALSE)
|
||||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
|
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
|
||||||
@@ -164,6 +167,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
final Integer graceOnPrincipalPayment = command.integerValueOfParameterNamed("graceOnPrincipalPayment");
|
final Integer graceOnPrincipalPayment = command.integerValueOfParameterNamed("graceOnPrincipalPayment");
|
||||||
final Integer graceOnInterestPayment = command.integerValueOfParameterNamed("graceOnInterestPayment");
|
final Integer graceOnInterestPayment = command.integerValueOfParameterNamed("graceOnInterestPayment");
|
||||||
final Integer graceOnInterestCharged = command.integerValueOfParameterNamed("graceOnInterestCharged");
|
final Integer graceOnInterestCharged = command.integerValueOfParameterNamed("graceOnInterestCharged");
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = command
|
||||||
|
.integerValueOfParameterNamed("minimumDaysBetweenDisbursalAndFirstRepayment");
|
||||||
|
|
||||||
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
|
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
|
||||||
final boolean includeInBorrowerCycle = command.booleanPrimitiveValueOfParameterNamed("includeInBorrowerCycle");
|
final boolean includeInBorrowerCycle = command.booleanPrimitiveValueOfParameterNamed("includeInBorrowerCycle");
|
||||||
@@ -213,7 +218,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
graceOnInterestCharged, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle,
|
graceOnInterestCharged, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle,
|
||||||
startDate, closeDate, externalId, useBorrowerCycle, loanProductBorrowerCycleVariations, multiDisburseLoan, maxTrancheCount,
|
startDate, closeDate, externalId, useBorrowerCycle, loanProductBorrowerCycleVariations, multiDisburseLoan, maxTrancheCount,
|
||||||
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
|
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
|
||||||
isInterestRecalculationEnabled, interestRecalculationSettings);
|
isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +435,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan,
|
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan,
|
||||||
final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
|
final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
|
||||||
final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
|
final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
|
||||||
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationDetails productInterestRecalculationDetails) {
|
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationDetails productInterestRecalculationDetails,
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment) {
|
||||||
this.fund = fund;
|
this.fund = fund;
|
||||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||||
this.name = name.trim();
|
this.name = name.trim();
|
||||||
@@ -479,6 +485,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
this.loanProducTrancheDetails = new LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, outstandingLoanBalance);
|
this.loanProducTrancheDetails = new LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, outstandingLoanBalance);
|
||||||
this.overdueDaysForNPA = overdueDaysForNPA;
|
this.overdueDaysForNPA = overdueDaysForNPA;
|
||||||
this.productInterestRecalculationDetails = productInterestRecalculationDetails;
|
this.productInterestRecalculationDetails = productInterestRecalculationDetails;
|
||||||
|
this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonetaryCurrency getCurrency() {
|
public MonetaryCurrency getCurrency() {
|
||||||
@@ -650,6 +657,15 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
this.overdueDaysForNPA = newValue;
|
this.overdueDaysForNPA = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment,
|
||||||
|
this.minimumDaysBetweenDisbursalAndFirstRepayment)) {
|
||||||
|
final Integer newValue = command
|
||||||
|
.integerValueOfParameterNamed(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
|
actualChanges.put(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, newValue);
|
||||||
|
actualChanges.put("locale", localeAsInput);
|
||||||
|
this.minimumDaysBetweenDisbursalAndFirstRepayment = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update interest recalculation settings
|
* Update interest recalculation settings
|
||||||
*/
|
*/
|
||||||
@@ -793,6 +809,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
return this.loanProductRelatedDetail.isInterestRecalculationEnabled();
|
return this.loanProductRelatedDetail.isInterestRecalculationEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getMinimumDaysBetweenDisbursalAndFirstRepayment(){
|
||||||
|
return this.minimumDaysBetweenDisbursalAndFirstRepayment == null?0:this.minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
}
|
||||||
|
|
||||||
public LoanProductBorrowerCycleVariations fetchLoanProductBorrowerCycleVariationById(Long id) {
|
public LoanProductBorrowerCycleVariations fetchLoanProductBorrowerCycleVariationById(Long id) {
|
||||||
LoanProductBorrowerCycleVariations borrowerCycleVariation = null;
|
LoanProductBorrowerCycleVariations borrowerCycleVariation = null;
|
||||||
for (LoanProductBorrowerCycleVariations cycleVariation : this.borrowerCycleVariations) {
|
for (LoanProductBorrowerCycleVariations cycleVariation : this.borrowerCycleVariations) {
|
||||||
|
|||||||
+10
-2
@@ -70,7 +70,8 @@ public final class LoanProductDataValidator {
|
|||||||
LoanProductConstants.interestRecalculationCompoundingMethodParameterName,
|
LoanProductConstants.interestRecalculationCompoundingMethodParameterName,
|
||||||
LoanProductConstants.recalculationRestFrequencyDateParamName,
|
LoanProductConstants.recalculationRestFrequencyDateParamName,
|
||||||
LoanProductConstants.recalculationRestFrequencyIntervalParameterName,
|
LoanProductConstants.recalculationRestFrequencyIntervalParameterName,
|
||||||
LoanProductConstants.recalculationRestFrequencyTypeParameterName));
|
LoanProductConstants.recalculationRestFrequencyTypeParameterName,
|
||||||
|
LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment));
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
@@ -103,7 +104,14 @@ public final class LoanProductDataValidator {
|
|||||||
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
|
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
|
||||||
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists("minimumDaysBetweenDisbursalAndFirstRepayment", element)) {
|
||||||
|
final Long minimumDaysBetweenDisbursalAndFirstRepayment = this.fromApiJsonHelper.extractLongNamed(
|
||||||
|
"minimumDaysBetweenDisbursalAndFirstRepayment", element);
|
||||||
|
baseDataValidator.reset().parameter("minimumDaysBetweenDisbursalAndFirstRepayment")
|
||||||
|
.value(minimumDaysBetweenDisbursalAndFirstRepayment).ignoreIfNull().integerGreaterThanZero();
|
||||||
|
}
|
||||||
|
|
||||||
final Boolean includeInBorrowerCycle = this.fromApiJsonHelper.extractBooleanNamed("includeInBorrowerCycle", element);
|
final Boolean includeInBorrowerCycle = this.fromApiJsonHelper.extractBooleanNamed("includeInBorrowerCycle", element);
|
||||||
baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull()
|
baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull()
|
||||||
.validateForBooleanValue();
|
.validateForBooleanValue();
|
||||||
|
|||||||
+3
-1
@@ -120,6 +120,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
|
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
|
||||||
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
|
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
|
||||||
+ "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,lp.grace_on_arrears_ageing as graceOnArrearsAgeing,lp.overdue_days_for_npa as overdueDaysForNPA, "
|
+ "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,lp.grace_on_arrears_ageing as graceOnArrearsAgeing,lp.overdue_days_for_npa as overdueDaysForNPA, "
|
||||||
|
+ "lp.min_days_between_disbursal_and_first_repayment As minimumDaysBetweenDisbursalAndFirstRepayment, "
|
||||||
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
|
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
|
||||||
+ "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, "
|
+ "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, "
|
||||||
+ "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, "
|
+ "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, "
|
||||||
@@ -172,6 +173,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
final Integer graceOnInterestCharged = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestCharged");
|
final Integer graceOnInterestCharged = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestCharged");
|
||||||
final Integer graceOnArrearsAgeing = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnArrearsAgeing");
|
final Integer graceOnArrearsAgeing = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnArrearsAgeing");
|
||||||
final Integer overdueDaysForNPA = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "overdueDaysForNPA");
|
final Integer overdueDaysForNPA = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "overdueDaysForNPA");
|
||||||
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = JdbcSupport.getInteger(rs,"minimumDaysBetweenDisbursalAndFirstRepayment");
|
||||||
|
|
||||||
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
|
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
|
||||||
final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId);
|
final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId);
|
||||||
@@ -263,7 +265,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
closeDate, status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle,
|
closeDate, status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle,
|
||||||
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
||||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
interestRecalculationData);
|
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `m_product_loan`
|
||||||
|
ADD COLUMN `min_days_between_disbursal_and_first_repayment` INT(3) NULL AFTER `interest_recalculation_enabled`;
|
||||||
Reference in New Issue
Block a user