Merge branch 'reschedule_center' into develop
This commit is contained in:
+392
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.apache.fineract.integrationtests.common.CalendarHelper;
|
||||
import org.apache.fineract.integrationtests.common.CenterDomain;
|
||||
import org.apache.fineract.integrationtests.common.CenterHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.GroupHelper;
|
||||
import org.apache.fineract.integrationtests.common.OfficeHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.integrationtests.common.organisation.StaffHelper;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
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;
|
||||
|
||||
public class LoanReschedulingWithinCenterTest {
|
||||
|
||||
private RequestSpecification requestSpec;
|
||||
private ResponseSpecification responseSpec;
|
||||
private LoanTransactionHelper loanTransactionHelper;
|
||||
private ResponseSpecification generalResponseSpec;
|
||||
private LoanApplicationApprovalTest loanApplicationApprovalTest;
|
||||
|
||||
@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("rawtypes")
|
||||
@Test
|
||||
public void testCenterReschedulingLoansWithInterestRecalculationEnabled() {
|
||||
|
||||
Integer officeId = new OfficeHelper(requestSpec, responseSpec).createOffice("01 July 2007");
|
||||
String name = "TestFullCreation" + new Timestamp(new java.util.Date().getTime());
|
||||
String externalId = Utils.randomStringGenerator("ID_", 7, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
int staffId = StaffHelper.createStaff(requestSpec, responseSpec);
|
||||
int[] groupMembers = generateGroupMembers(1, officeId);
|
||||
final String centerActivationDate = "01 July 2007";
|
||||
Integer centerId = CenterHelper.createCenter(name, officeId, externalId, staffId, groupMembers, centerActivationDate, requestSpec,
|
||||
responseSpec);
|
||||
CenterDomain center = CenterHelper.retrieveByID(centerId, requestSpec, responseSpec);
|
||||
Integer groupId = groupMembers[0];
|
||||
Assert.assertNotNull(center);
|
||||
Assert.assertTrue(center.getStaffId() == staffId);
|
||||
Assert.assertTrue(center.isActive() == true);
|
||||
|
||||
Integer calendarId = createCalendarMeeting(centerId);
|
||||
|
||||
Integer clientId = createClient(officeId);
|
||||
|
||||
associateClientsToGroup(groupId, clientId);
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
|
||||
dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
|
||||
Calendar today = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
today.add(Calendar.DAY_OF_MONTH, -14);
|
||||
// CREATE A LOAN PRODUCT
|
||||
final String disbursalDate = dateFormat.format(today.getTime());
|
||||
final String recalculationRestFrequencyDate = "01 January 2012";
|
||||
final boolean isMultiTrancheLoan = false;
|
||||
|
||||
// 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, isMultiTrancheLoan);
|
||||
|
||||
// APPLY FOR TRANCHE LOAN WITH INTEREST RECALCULATION
|
||||
final Integer loanId = applyForLoanApplicationForInterestRecalculation(clientId, groupId, calendarId, loanProductID, disbursalDate,
|
||||
recalculationRestFrequencyDate, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList<HashMap>(0), null);
|
||||
|
||||
// Test for loan account is created
|
||||
Assert.assertNotNull(loanId);
|
||||
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanId);
|
||||
|
||||
// Test for loan account is created, can be approved
|
||||
this.loanTransactionHelper.approveLoan(disbursalDate, loanId);
|
||||
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanId);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
|
||||
// Test for loan account approved can be disbursed
|
||||
this.loanTransactionHelper.disburseLoan(disbursalDate, loanId);
|
||||
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanId);
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
System.out.println("---------------------------------CHANGING GROUP MEETING DATE ------------------------------------------");
|
||||
Calendar todaysdate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
todaysdate.add(Calendar.DAY_OF_MONTH, 14);
|
||||
String oldMeetingDate = dateFormat.format(todaysdate.getTime());
|
||||
todaysdate.add(Calendar.DAY_OF_MONTH, 1);
|
||||
final String centerMeetingNewStartDate = dateFormat.format(todaysdate.getTime());
|
||||
CalendarHelper.updateMeetingCalendarForCenter(this.requestSpec, this.responseSpec, centerId, calendarId.toString(), oldMeetingDate,
|
||||
centerMeetingNewStartDate);
|
||||
|
||||
ArrayList loanRepaymnetSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec, loanId);
|
||||
// VERIFY RESCHEDULED DATE
|
||||
ArrayList dueDateLoanSchedule = (ArrayList) ((HashMap) loanRepaymnetSchedule.get(2)).get("dueDate");
|
||||
assertEquals(getDateAsArray(todaysdate, 0), dueDateLoanSchedule);
|
||||
|
||||
// VERIFY THE INTEREST
|
||||
Float interestDue = (Float) ((HashMap) loanRepaymnetSchedule.get(2)).get("interestDue");
|
||||
assertEquals(String.valueOf(interestDue), "90.82");
|
||||
|
||||
}
|
||||
|
||||
private void associateClientsToGroup(Integer groupId, Integer clientId) {
|
||||
// Associate client to the group
|
||||
GroupHelper.associateClient(this.requestSpec, this.responseSpec, groupId.toString(), clientId.toString());
|
||||
GroupHelper.verifyGroupMembers(this.requestSpec, this.responseSpec, groupId, clientId);
|
||||
}
|
||||
|
||||
private Integer createClient(Integer officeId) {
|
||||
// CREATE CLIENT
|
||||
final String clientActivationDate = "01 July 2014";
|
||||
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, clientActivationDate, officeId.toString());
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientId);
|
||||
return clientId;
|
||||
}
|
||||
|
||||
private Integer createCalendarMeeting(Integer centerId) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
|
||||
dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
|
||||
Calendar today = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
final String startDate = dateFormat.format(today.getTime());
|
||||
final String frequency = "2"; // 2:Weekly
|
||||
final String interval = "2"; // Every one week
|
||||
final Integer repeatsOnDay = today.get(Calendar.DAY_OF_WEEK) - 1;
|
||||
|
||||
Integer calendarId = CalendarHelper.createMeetingForGroup(this.requestSpec, this.responseSpec, centerId, startDate, frequency,
|
||||
interval, repeatsOnDay.toString());
|
||||
System.out.println("calendarId " + calendarId);
|
||||
return calendarId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void testCenterReschedulingMultiTrancheLoansWithInterestRecalculationEnabled() {
|
||||
|
||||
Integer officeId = new OfficeHelper(requestSpec, responseSpec).createOffice("01 July 2007");
|
||||
String name = "TestFullCreation" + new Timestamp(new java.util.Date().getTime());
|
||||
String externalId = Utils.randomStringGenerator("ID_", 7, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
int staffId = StaffHelper.createStaff(requestSpec, responseSpec);
|
||||
int[] groupMembers = generateGroupMembers(1, officeId);
|
||||
final String centerActivationDate = "01 July 2007";
|
||||
Integer centerId = CenterHelper.createCenter(name, officeId, externalId, staffId, groupMembers, centerActivationDate, requestSpec,
|
||||
responseSpec);
|
||||
CenterDomain center = CenterHelper.retrieveByID(centerId, requestSpec, responseSpec);
|
||||
Integer groupId = groupMembers[0];
|
||||
Assert.assertNotNull(center);
|
||||
Assert.assertTrue(center.getStaffId() == staffId);
|
||||
Assert.assertTrue(center.isActive() == true);
|
||||
|
||||
Integer calendarId = createCalendarMeeting(centerId);
|
||||
|
||||
Integer clientId = createClient(officeId);
|
||||
|
||||
associateClientsToGroup(groupId, clientId);
|
||||
|
||||
// CREATE A LOAN PRODUCT
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
|
||||
dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
|
||||
Calendar today = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
today.add(Calendar.DAY_OF_MONTH, -14);
|
||||
// CREATE A LOAN PRODUCT
|
||||
final String approveDate = dateFormat.format(today.getTime());
|
||||
final String expectedDisbursementDate = dateFormat.format(today.getTime());
|
||||
final String disbursementDate = dateFormat.format(today.getTime());
|
||||
final String approvalAmount = "10000";
|
||||
final String recalculationRestFrequencyDate = "01 January 2012";
|
||||
final boolean isMultiTrancheLoan = true;
|
||||
|
||||
// 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, isMultiTrancheLoan);
|
||||
|
||||
// CREATE TRANCHES
|
||||
List<HashMap> createTranches = new ArrayList<>();
|
||||
createTranches.add(this.loanApplicationApprovalTest.createTrancheDetail(disbursementDate, "5000"));
|
||||
createTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("25 June 2016", "5000"));
|
||||
|
||||
// APPROVE TRANCHES
|
||||
List<HashMap> approveTranches = new ArrayList<>();
|
||||
approveTranches.add(this.loanApplicationApprovalTest.createTrancheDetail(disbursementDate, "5000"));
|
||||
approveTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("25 June 2016", "5000"));
|
||||
|
||||
// APPLY FOR TRANCHE LOAN WITH INTEREST RECALCULATION
|
||||
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientId, groupId, calendarId, 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.disburseLoan(disbursementDate, loanID);
|
||||
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||
|
||||
System.out.println("---------------------------------CHANGING GROUP MEETING DATE ------------------------------------------");
|
||||
Calendar todaysdate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
todaysdate.add(Calendar.DAY_OF_MONTH, 14);
|
||||
String oldMeetingDate = dateFormat.format(todaysdate.getTime());
|
||||
todaysdate.add(Calendar.DAY_OF_MONTH, 1);
|
||||
final String centerMeetingNewStartDate = dateFormat.format(todaysdate.getTime());
|
||||
CalendarHelper.updateMeetingCalendarForCenter(this.requestSpec, this.responseSpec, centerId, calendarId.toString(), oldMeetingDate,
|
||||
centerMeetingNewStartDate);
|
||||
|
||||
ArrayList loanRepaymnetSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec, loanID);
|
||||
// VERIFY RESCHEDULED DATE
|
||||
ArrayList dueDateLoanSchedule = (ArrayList) ((HashMap) loanRepaymnetSchedule.get(2)).get("dueDate");
|
||||
assertEquals(getDateAsArray(todaysdate, 0), dueDateLoanSchedule);
|
||||
|
||||
// VERIFY THE INTEREST
|
||||
Float interestDue = (Float) ((HashMap) loanRepaymnetSchedule.get(2)).get("interestDue");
|
||||
assertEquals(String.valueOf(interestDue), "41.05");
|
||||
|
||||
}
|
||||
|
||||
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 boolean isMultiTrancheLoan) {
|
||||
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, isMultiTrancheLoan);
|
||||
}
|
||||
|
||||
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, final boolean isMultiTrancheLoan) {
|
||||
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
LoanProductTestBuilder builder = new LoanProductTestBuilder()
|
||||
.withPrincipal("10000.00")
|
||||
.withNumberOfRepayments("12")
|
||||
.withRepaymentAfterEvery("2")
|
||||
.withRepaymentTypeAsWeek()
|
||||
.withinterestRatePerPeriod("2")
|
||||
.withInterestRateFrequencyTypeAsMonths()
|
||||
.withTranches(isMultiTrancheLoan)
|
||||
.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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, Integer groupId, Integer calendarId,
|
||||
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, groupId, calendarId, loanProductID, disbursementDate,
|
||||
restStartDate, compoundingStartDate, repaymentStrategy, charges, graceOnInterestPayment, graceOnPrincipalPayment, tranches);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unused" })
|
||||
private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, Integer groupId, Integer calendarId,
|
||||
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").withLoanType("jlg") //
|
||||
.withCalendarID(calendarId.toString()).withAmortizationTypeAsEqualInstallments() //
|
||||
.withFixedEmiAmount("") //
|
||||
.withTranches(tranches).withInterestTypeAsDecliningBalance() //
|
||||
.withInterestCalculationPeriodTypeAsDays() //
|
||||
.withInterestCalculationPeriodTypeAsDays() //
|
||||
.withExpectedDisbursementDate(disbursementDate) //
|
||||
.withSubmittedOnDate(disbursementDate) //
|
||||
.withRestFrequencyDate(restStartDate)//
|
||||
.withwithRepaymentStrategy(repaymentStrategy) //
|
||||
.withCharges(charges)//
|
||||
.build(clientID.toString(), groupId.toString(), loanProductID.toString(), null);
|
||||
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
}
|
||||
|
||||
private int[] generateGroupMembers(int size, int officeId) {
|
||||
int[] groupMembers = new int[size];
|
||||
for (int i = 0; i < groupMembers.length; i++) {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("officeId", "" + officeId);
|
||||
map.put("name", Utils.randomStringGenerator("Group_Name_", 5));
|
||||
map.put("externalId", Utils.randomStringGenerator("ID_", 7, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
|
||||
map.put("dateFormat", "dd MMMM yyyy");
|
||||
map.put("locale", "en");
|
||||
map.put("active", "true");
|
||||
map.put("activationDate", "04 March 2011");
|
||||
|
||||
groupMembers[i] = Utils.performServerPost(requestSpec, responseSpec, "/fineract-provider/api/v1/groups?"
|
||||
+ Utils.TENANT_IDENTIFIER, new Gson().toJson(map), "groupId");
|
||||
}
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
+41
@@ -32,6 +32,8 @@ public class CalendarHelper {
|
||||
private static final String BASE_URL = "/fineract-provider/api/v1/";
|
||||
private static final String PARENT_ENTITY_NAME = "groups/";
|
||||
private static final String ENITY_NAME = "/calendars";
|
||||
private static final String Center_Entity = "centers/";
|
||||
private static final String Edit_Calendar = "editcalendarbasedonmeetingdates/";
|
||||
|
||||
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) {
|
||||
@@ -86,4 +88,43 @@ public class CalendarHelper {
|
||||
final Integer responseCalendarId = from(responseCalendarDetailsinJSON).get("id");
|
||||
assertEquals("ERROR IN CREATING THE CALENDAR", generatedCalendarId, responseCalendarId);
|
||||
}
|
||||
|
||||
public static Integer createMeetingForGroup(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 + Center_Entity + 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 Integer updateMeetingCalendarForCenter(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
Integer centerId, String calendarID, String oldDate, String startDate) {
|
||||
|
||||
System.out.println("---------------------------------UPADATING A MEETING CALENDAR FOR THE CENTER------------------------------");
|
||||
|
||||
final String CALENDAR_RESOURCE_URL = BASE_URL + Center_Entity + centerId + ENITY_NAME + "/" + calendarID + "?"
|
||||
+ Utils.TENANT_IDENTIFIER;
|
||||
|
||||
System.out.println(CALENDAR_RESOURCE_URL);
|
||||
|
||||
return Utils.performServerPut(requestSpec, responseSpec, CALENDAR_RESOURCE_URL, getTestCalendarMeetingAsJSON(oldDate, startDate),
|
||||
"resourceId");
|
||||
|
||||
}
|
||||
|
||||
private static String getTestCalendarMeetingAsJSON(String oldDate, String startDate) {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("dateFormat", "dd MMMM yyyy");
|
||||
map.put("locale", "en");
|
||||
map.put("newMeetingDate", startDate);
|
||||
map.put("presentMeetingDate", oldDate);
|
||||
map.put("reschedulebasedOnMeetingDates", "true");
|
||||
System.out.println("map : " + map);
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.calendar.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
|
||||
public class CalendarHistoryDataWrapper {
|
||||
|
||||
private final List<CalendarHistory> calendarHistoryList;
|
||||
|
||||
public CalendarHistoryDataWrapper(final Set<CalendarHistory> calendarHistoryList){
|
||||
this.calendarHistoryList = new ArrayList<>();
|
||||
this.calendarHistoryList.addAll(calendarHistoryList);
|
||||
final Comparator<CalendarHistory> orderByDate = new Comparator<CalendarHistory>() {
|
||||
@Override
|
||||
public int compare(CalendarHistory calendarHistory1, CalendarHistory calendarHistory2) {
|
||||
return calendarHistory1.getEndDateLocalDate().compareTo(calendarHistory2.getEndDateLocalDate());
|
||||
}
|
||||
};
|
||||
Collections.sort(this.calendarHistoryList, orderByDate);
|
||||
}
|
||||
|
||||
public CalendarHistory getCalendarHistory(final LocalDate dueRepaymentPeriodDate) {
|
||||
CalendarHistory calendarHistory = null;
|
||||
for (CalendarHistory history : this.calendarHistoryList) {
|
||||
if (history.getEndDateLocalDate().isAfter(dueRepaymentPeriodDate)) {
|
||||
calendarHistory = history;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return calendarHistory;
|
||||
}
|
||||
|
||||
public List<CalendarHistory> getCalendarHistoryList(){
|
||||
return this.calendarHistoryList;
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -95,7 +95,7 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "calendar_id")
|
||||
private final Set<CalendarHistory> calendarHistory = new HashSet<>();
|
||||
private Set<CalendarHistory> calendarHistory = new HashSet<>();
|
||||
|
||||
protected Calendar() {
|
||||
|
||||
@@ -581,4 +581,12 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.startDate = startDate.toDate();
|
||||
this.endDate = endDate.toDate();
|
||||
}
|
||||
|
||||
public Set<CalendarHistory> getCalendarHistory(){
|
||||
return this.calendarHistory;
|
||||
}
|
||||
|
||||
public void updateCalendarHistory(final Set<CalendarHistory> calendarHistory){
|
||||
this.calendarHistory = calendarHistory;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -24,6 +24,7 @@ import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
@@ -239,7 +240,6 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
|
||||
if (calendarForUpdate == null) { throw new CalendarNotFoundException(calendarId); }
|
||||
|
||||
final Date oldStartDate = calendarForUpdate.getStartDate();
|
||||
final LocalDate currentDate = DateUtils.getLocalDateOfTenant();
|
||||
// create calendar history before updating calendar
|
||||
final CalendarHistory calendarHistory = new CalendarHistory(calendarForUpdate, oldStartDate);
|
||||
|
||||
@@ -288,12 +288,12 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
|
||||
if (!changes.isEmpty()) {
|
||||
// update calendar history table only if there is a change in
|
||||
// calendar start date.
|
||||
if (currentDate.isAfter(new LocalDate(oldStartDate))) {
|
||||
final Date endDate = calendarForUpdate.getStartDateLocalDate().minusDays(1).toDate();
|
||||
calendarHistory.updateEndDate(endDate);
|
||||
this.calendarHistoryRepository.save(calendarHistory);
|
||||
}
|
||||
|
||||
final Date endDate = presentMeetingDate.minusDays(1).toDate();
|
||||
calendarHistory.updateEndDate(endDate);
|
||||
this.calendarHistoryRepository.save(calendarHistory);
|
||||
Set<CalendarHistory> history = calendarForUpdate.getCalendarHistory();
|
||||
history.add(calendarHistory);
|
||||
calendarForUpdate.updateCalendarHistory(history);
|
||||
this.calendarRepository.saveAndFlush(calendarForUpdate);
|
||||
|
||||
if (this.configurationDomainService.isRescheduleFutureRepaymentsEnabled() && calendarForUpdate.isRepeating()) {
|
||||
|
||||
+17
-1
@@ -19,6 +19,8 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.data;
|
||||
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
@@ -35,11 +37,14 @@ public class ScheduleGeneratorDTO {
|
||||
LocalDate recalculateFrom;
|
||||
final Long overdurPenaltyWaitPeriod;
|
||||
final FloatingRateDTO floatingRateDTO;
|
||||
final Calendar calendar;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper;
|
||||
|
||||
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, final CalendarInstance compoundingCalendarInstance,
|
||||
final LocalDate recalculateFrom, final Long overdurPenaltyWaitPeriod, final FloatingRateDTO floatingRateDTO) {
|
||||
final LocalDate recalculateFrom, final Long overdurPenaltyWaitPeriod, final FloatingRateDTO floatingRateDTO,
|
||||
final Calendar calendar, final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
||||
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.applicationCurrency = applicationCurrency;
|
||||
@@ -50,6 +55,9 @@ public class ScheduleGeneratorDTO {
|
||||
this.overdurPenaltyWaitPeriod = overdurPenaltyWaitPeriod;
|
||||
this.holidayDetailDTO = holidayDetailDTO;
|
||||
this.floatingRateDTO = floatingRateDTO;
|
||||
this.calendar = calendar;
|
||||
this.calendarHistoryDataWrapper = calendarHistoryDataWrapper;
|
||||
|
||||
}
|
||||
|
||||
public LoanScheduleGeneratorFactory getLoanScheduleFactory() {
|
||||
@@ -99,5 +107,13 @@ public class ScheduleGeneratorDTO {
|
||||
public FloatingRateDTO getFloatingRateDTO() {
|
||||
return this.floatingRateDTO;
|
||||
}
|
||||
|
||||
public Calendar getCalendar(){
|
||||
return this.calendar;
|
||||
}
|
||||
|
||||
public CalendarHistoryDataWrapper getCalendarHistoryDataWrapper(){
|
||||
return this.calendarHistoryDataWrapper;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-2
@@ -71,7 +71,9 @@ import org.apache.fineract.organisation.staff.domain.Staff;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
@@ -5032,6 +5034,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
InterestRecalculationCompoundingMethod compoundingMethod = null;
|
||||
RecalculationFrequencyType compoundingFrequencyType = null;
|
||||
LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
Calendar calendar = null;
|
||||
CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = scheduleGeneratorDTO.getCalendarInstanceForInterestRecalculation();
|
||||
compoundingCalendarInstance = scheduleGeneratorDTO.getCompoundingCalendarInstance();
|
||||
@@ -5039,6 +5043,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
compoundingMethod = this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod();
|
||||
compoundingFrequencyType = this.loanInterestRecalculationDetails.getCompoundingFrequencyType();
|
||||
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
calendar = scheduleGeneratorDTO.getCalendar();
|
||||
calendarHistoryDataWrapper = scheduleGeneratorDTO.getCalendarHistoryDataWrapper();
|
||||
}
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
@@ -5053,7 +5059,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
this.maxOutstandingLoanBalance, getInterestChargedFromDate(), this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations);
|
||||
rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations, calendarHistoryDataWrapper);
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
@@ -5269,6 +5275,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance();
|
||||
|
||||
final List<DisbursementData> disbursementData = getDisbursmentData();
|
||||
|
||||
CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
if (loanCalendar != null) {
|
||||
Set<CalendarHistory> calendarHistory = loanCalendar.getCalendarHistory();
|
||||
calendarHistoryDataWrapper = new CalendarHistoryDataWrapper(calendarHistory);
|
||||
}
|
||||
|
||||
RecalculationFrequencyType recalculationFrequencyType = null;
|
||||
InterestRecalculationCompoundingMethod compoundingMethod = null;
|
||||
@@ -5290,7 +5302,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
maxOutstandingBalance, interestChargedFromDate, this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations);
|
||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+22
-3
@@ -18,10 +18,13 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
|
||||
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
|
||||
import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
|
||||
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
@@ -68,9 +71,25 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
// calendar associated with
|
||||
// the loan, and we should use it in order to calculate next
|
||||
// repayment
|
||||
LocalDate seedDate = currentCalendar.getStartDateLocalDate();
|
||||
String reccuringString = currentCalendar.getRecurrence();
|
||||
dueRepaymentPeriodDate = CalendarUtils.getNewRepaymentMeetingDate(reccuringString, seedDate, dueRepaymentPeriodDate,
|
||||
|
||||
CalendarHistory calendarHistory = null;
|
||||
CalendarHistoryDataWrapper calendarHistoryDataWrapper = loanApplicationTerms.getCalendarHistoryDataWrapper();
|
||||
if(calendarHistoryDataWrapper != null){
|
||||
calendarHistory = loanApplicationTerms.getCalendarHistoryDataWrapper().getCalendarHistory(dueRepaymentPeriodDate);
|
||||
}
|
||||
|
||||
// get the start date from the calendar history
|
||||
LocalDate seedDate = null;
|
||||
String reccuringString = null;
|
||||
if (calendarHistory == null) {
|
||||
seedDate = currentCalendar.getStartDateLocalDate();
|
||||
reccuringString = currentCalendar.getRecurrence();
|
||||
} else {
|
||||
seedDate = calendarHistory.getStartDateLocalDate();
|
||||
reccuringString = calendarHistory.getRecurrence();
|
||||
}
|
||||
|
||||
dueRepaymentPeriodDate = CalendarUtils.getNewRepaymentMeetingDate(reccuringString, seedDate, lastRepaymentDate.plusDays(1),
|
||||
loanApplicationTerms.getRepaymentEvery(),
|
||||
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(loanApplicationTerms.getLoanTermPeriodFrequencyType()),
|
||||
holidayDetailDTO.getWorkingDays());
|
||||
|
||||
+22
-10
@@ -27,6 +27,7 @@ import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
|
||||
@@ -177,6 +178,8 @@ public final class LoanApplicationTerms {
|
||||
private Money adjustPrincipalForFlatLoans;
|
||||
|
||||
private final LocalDate seedDate;
|
||||
|
||||
private final CalendarHistoryDataWrapper calendarHistoryDataWrapper;
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
@@ -198,6 +201,7 @@ public final class LoanApplicationTerms {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
return new LoanApplicationTerms(currency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery,
|
||||
repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod, interestRatePerPeriod,
|
||||
interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||
@@ -207,7 +211,7 @@ public final class LoanApplicationTerms {
|
||||
graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
|
||||
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -224,13 +228,14 @@ public final class LoanApplicationTerms {
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount, BigDecimal annualNominalInterestRate,
|
||||
List<LoanTermVariationsData> loanTermVariations) {
|
||||
final Calendar loanCalendar = null;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
|
||||
return assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDay, dayOfWeek,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsTolerance,
|
||||
loanProductRelatedDetail, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, interestChargedFromDate,
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType, restCalendarInstance,
|
||||
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
|
||||
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations);
|
||||
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations, calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -245,7 +250,8 @@ public final class LoanApplicationTerms {
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount,
|
||||
BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations) {
|
||||
BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations,
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -277,7 +283,7 @@ public final class LoanApplicationTerms {
|
||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -289,8 +295,8 @@ public final class LoanApplicationTerms {
|
||||
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, BigDecimal approvedAmount,
|
||||
final BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations) {
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
||||
BigDecimal approvedAmount, final BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -319,7 +325,8 @@ public final class LoanApplicationTerms {
|
||||
rescheduleStrategyMethod = interestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
interestRecalculationCompoundingMethod = interestRecalculationDetails.getInterestRecalculationCompoundingMethod();
|
||||
}
|
||||
final Calendar loanCalendar = null;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
|
||||
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
||||
repaymentEvery, repaymentPeriodFrequencyType, null, null, amortizationMethod, interestMethod, interestRatePerPeriod,
|
||||
interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||
@@ -329,7 +336,7 @@ public final class LoanApplicationTerms {
|
||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final LoanApplicationTerms applicationTerms,
|
||||
@@ -351,7 +358,7 @@ public final class LoanApplicationTerms {
|
||||
applicationTerms.compoundingCalendarInstance, applicationTerms.compoundingFrequencyType,
|
||||
applicationTerms.principalThresholdForLastInstalment, applicationTerms.installmentAmountInMultiplesOf,
|
||||
applicationTerms.preClosureInterestCalculationStrategy, applicationTerms.loanCalendar,
|
||||
applicationTerms.approvedPrincipal.getAmount(), loanTermVariations);
|
||||
applicationTerms.approvedPrincipal.getAmount(), loanTermVariations, applicationTerms.calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -372,7 +379,7 @@ public final class LoanApplicationTerms {
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations) {
|
||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations, final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
||||
@@ -430,6 +437,7 @@ public final class LoanApplicationTerms {
|
||||
} else {
|
||||
this.seedDate = this.calculatedRepaymentsStartingFromDate;
|
||||
}
|
||||
this.calendarHistoryDataWrapper = calendarHistoryDataWrapper;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -1421,5 +1429,9 @@ public final class LoanApplicationTerms {
|
||||
public LocalDate getSeedDate() {
|
||||
return this.seedDate;
|
||||
}
|
||||
|
||||
public CalendarHistoryDataWrapper getCalendarHistoryDataWrapper(){
|
||||
return this.calendarHistoryDataWrapper;
|
||||
}
|
||||
|
||||
}
|
||||
+29
-7
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
@@ -30,8 +31,10 @@ import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepos
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
@@ -88,8 +91,15 @@ public class LoanUtilService {
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
Calendar calendar = null;
|
||||
CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
if (calendarInstance != null) {
|
||||
calendar = calendarInstance.getCalendar();
|
||||
Set<CalendarHistory> calendarHistory = calendar.getCalendarHistory();
|
||||
calendarHistoryDataWrapper = new CalendarHistoryDataWrapper(calendarHistory);
|
||||
}
|
||||
LocalDate calculatedRepaymentsStartingFromDate = this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan,
|
||||
calendarInstance);
|
||||
calendarInstance, calendarHistoryDataWrapper);
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
@@ -103,7 +113,7 @@ public class LoanUtilService {
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO, calendar, calendarHistoryDataWrapper);
|
||||
|
||||
return scheduleGeneratorDTO;
|
||||
}
|
||||
@@ -111,7 +121,8 @@ public class LoanUtilService {
|
||||
public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) {
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
return this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance);
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
return this.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance, calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
private HolidayDetailDTO constructHolidayDTO(final Loan loan) {
|
||||
@@ -146,22 +157,33 @@ public class LoanUtilService {
|
||||
}
|
||||
|
||||
private LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan,
|
||||
final CalendarInstance calendarInstance) {
|
||||
final CalendarInstance calendarInstance, final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
||||
final Calendar calendar = calendarInstance == null ? null : calendarInstance.getCalendar();
|
||||
return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar);
|
||||
return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar, calendarHistoryDataWrapper);
|
||||
}
|
||||
|
||||
public LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan,
|
||||
final Calendar calendar) {
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
if (calendar == null) { return getCalculatedRepaymentsStartingFromDate(loan); }
|
||||
return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar);
|
||||
return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar, calendarHistoryDataWrapper);
|
||||
|
||||
}
|
||||
|
||||
private LocalDate calculateRepaymentStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final Calendar calendar) {
|
||||
private LocalDate calculateRepaymentStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final Calendar calendar,
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
||||
LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
|
||||
if (calendar != null) {// sync repayments
|
||||
|
||||
if (calculatedRepaymentsStartingFromDate == null && !calendar.getCalendarHistory().isEmpty() &&
|
||||
calendarHistoryDataWrapper != null) {
|
||||
for (CalendarHistory calendarHistory : calendarHistoryDataWrapper.getCalendarHistoryList()) {
|
||||
calculatedRepaymentsStartingFromDate = calendarHistory.getStartDateLocalDate();
|
||||
break;
|
||||
}
|
||||
return calculatedRepaymentsStartingFromDate;
|
||||
}
|
||||
|
||||
// TODO: AA - user provided first repayment date takes precedence
|
||||
// over recalculated meeting date
|
||||
if (calculatedRepaymentsStartingFromDate == null) {
|
||||
|
||||
+20
-8
@@ -83,7 +83,6 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarRepository;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarType;
|
||||
import org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
@@ -130,8 +129,10 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachin
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanOverdueInstallmentCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
@@ -219,6 +220,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final GuarantorDomainService guarantorDomainService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanSummaryWrapper loanSummaryWrapper;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy;
|
||||
|
||||
@Autowired
|
||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -245,7 +248,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final AccountAssociationsRepository accountAssociationRepository,
|
||||
final AccountTransferDetailRepository accountTransferDetailRepository,
|
||||
final BusinessEventNotifierService businessEventNotifierService, final GuarantorDomainService guarantorDomainService,
|
||||
final LoanUtilService loanUtilService) {
|
||||
final LoanUtilService loanUtilService, final LoanSummaryWrapper loanSummaryWrapper,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy) {
|
||||
this.context = context;
|
||||
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
|
||||
this.loanAssembler = loanAssembler;
|
||||
@@ -280,6 +284,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.businessEventNotifierService = businessEventNotifierService;
|
||||
this.guarantorDomainService = guarantorDomainService;
|
||||
this.loanUtilService = loanUtilService;
|
||||
this.loanSummaryWrapper = loanSummaryWrapper;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
@@ -1955,6 +1961,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final AppUser currentUser = getAppUserIfPresent();
|
||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
final Collection<Integer> loanStatuses = new ArrayList<>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
|
||||
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue()));
|
||||
final Collection<Integer> loanTypes = new ArrayList<>(Arrays.asList(AccountType.GROUP.getValue(), AccountType.JLG.getValue()));
|
||||
@@ -1966,16 +1975,19 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final List<Loan> loans = this.loanRepository.findByIdsAndLoanStatusAndLoanType(loanIds, loanStatuses, loanTypes);
|
||||
List<Holiday> holidays = null;
|
||||
final LocalDate recalculateFrom = null;
|
||||
// loop through each loan to reschedule the repayment dates
|
||||
for (final Loan loan : loans) {
|
||||
if (loan != null) {
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
final String defaultUserMessage = "Meeting calendar type update is not supported";
|
||||
throw new CalendarParameterUpdateNotSupportedException("jlg.loan.recalculation", defaultUserMessage);
|
||||
}
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
|
||||
if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) {
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
loan.setHelpers(null, this.loanSummaryWrapper, this.transactionProcessingStrategy);
|
||||
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds,
|
||||
existingReversedTransactionIds, currentUser);
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
|
||||
loan.fetchRepaymentScheduleInstallments(), loan, null);
|
||||
} else if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) {
|
||||
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled,
|
||||
holidays, workingDays, reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user