Merge branch 'reschedule_center' into develop

This commit is contained in:
Nazeer Hussain Shaik
2016-03-02 17:59:18 +05:30
11 changed files with 635 additions and 39 deletions
@@ -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)));
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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()) {
@@ -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;
}
}
@@ -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);
}
/**
@@ -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());
@@ -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;
}
}
@@ -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) {
@@ -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 {