Reschedule repayment on holidays+extend term

This commit is contained in:
Terence Denzil Monteiro
2016-08-25 17:45:14 +05:30
parent 8327b112a1
commit b478064ac0
7 changed files with 86 additions and 21 deletions
@@ -36,14 +36,14 @@ public class WorkingDaysApiConstants {
public static final String rescheduleRepaymentTemplate = "rescheduleRepaymentTemplate"; public static final String rescheduleRepaymentTemplate = "rescheduleRepaymentTemplate";
public static final String localeParamName = "locale"; public static final String localeParamName = "locale";
public static final String extendTermForDailyRepayments = "extendTermForDailyRepayments"; public static final String extendTermForDailyRepayments = "extendTermForDailyRepayments";
public static final String extendTermForRepaymentsOnHolidays = "extendTermForRepaymentsOnHolidays";
public static final Set<String> WORKING_DAYS_CREATE_OR_UPDATE_REQUEST_DATA_PARAMETERS =new HashSet<>(Arrays.asList( public static final Set<String> WORKING_DAYS_CREATE_OR_UPDATE_REQUEST_DATA_PARAMETERS =new HashSet<>(Arrays.asList(
recurrence,repayment_rescheduling_enum,localeParamName,extendTermForDailyRepayments recurrence,repayment_rescheduling_enum,localeParamName,extendTermForDailyRepayments,extendTermForRepaymentsOnHolidays
)); ));
public static final Set<String> WORKING_DAYS_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName, public static final Set<String> WORKING_DAYS_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName,
recurrence,repayment_rescheduling_enum,extendTermForDailyRepayments recurrence,repayment_rescheduling_enum,extendTermForDailyRepayments,extendTermForRepaymentsOnHolidays
)); ));
public static final Set<String> WORKING_DAYS_TEMPLATE_PARAMETERS = new HashSet<>(Arrays.asList(rescheduleRepaymentTemplate)); public static final Set<String> WORKING_DAYS_TEMPLATE_PARAMETERS = new HashSet<>(Arrays.asList(rescheduleRepaymentTemplate));
@@ -67,14 +67,16 @@ public class WorkingDayValidator {
final Boolean extendTermForDailyRepayments = this.fromApiJsonHelper.extractBooleanNamed("extendTermForDailyRepayments", element); final Boolean extendTermForDailyRepayments = this.fromApiJsonHelper.extractBooleanNamed("extendTermForDailyRepayments", element);
baseDataValidator.reset().parameter(WorkingDaysApiConstants.extendTermForDailyRepayments).value(extendTermForDailyRepayments).ignoreIfNull().validateForBooleanValue(); baseDataValidator.reset().parameter(WorkingDaysApiConstants.extendTermForDailyRepayments).value(extendTermForDailyRepayments).ignoreIfNull().validateForBooleanValue();
final Boolean extendTermForRepaymentsOnHolidays = this.fromApiJsonHelper.extractBooleanNamed("extendTermForRepaymentsOnHolidays", element);
baseDataValidator.reset().parameter(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays).value(extendTermForRepaymentsOnHolidays).ignoreIfNull().validateForBooleanValue();
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
//
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
@@ -31,28 +31,33 @@ public class WorkingDaysData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final EnumOptionData repaymentRescheduleType; private final EnumOptionData repaymentRescheduleType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Boolean extendTermForDailyRepayments; private final Boolean extendTermForDailyRepayments;
@SuppressWarnings("unused")
private final Boolean extendTermForRepaymentsOnHolidays;
// template date // template date
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<EnumOptionData> repaymentRescheduleOptions; private final Collection<EnumOptionData> repaymentRescheduleOptions;
public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, Boolean extendTermForDailyRepayments) { public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, Boolean extendTermForDailyRepayments, Boolean extendTermForRepaymentsOnHolidays) {
this.id = id; this.id = id;
this.recurrence = recurrence; this.recurrence = recurrence;
this.repaymentRescheduleType = repaymentRescheduleType; this.repaymentRescheduleType = repaymentRescheduleType;
this.repaymentRescheduleOptions = null; this.repaymentRescheduleOptions = null;
this.extendTermForDailyRepayments = extendTermForDailyRepayments; this.extendTermForDailyRepayments = extendTermForDailyRepayments;
this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays;
} }
public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType,
Collection<EnumOptionData> repaymentRescheduleOptions, Boolean extendTermForDailyRepayments) { Collection<EnumOptionData> repaymentRescheduleOptions, Boolean extendTermForDailyRepayments, Boolean extendTermForRepaymentsOnHolidays) {
this.id = id; this.id = id;
this.recurrence = recurrence; this.recurrence = recurrence;
this.repaymentRescheduleType = repaymentRescheduleType; this.repaymentRescheduleType = repaymentRescheduleType;
this.repaymentRescheduleOptions = repaymentRescheduleOptions; this.repaymentRescheduleOptions = repaymentRescheduleOptions;
this.extendTermForDailyRepayments = extendTermForDailyRepayments; this.extendTermForDailyRepayments = extendTermForDailyRepayments;
this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays;
} }
} }
@@ -41,15 +41,19 @@ public class WorkingDays extends AbstractPersistable<Long> {
@Column(name = "extend_term_daily_repayments", nullable = false) @Column(name = "extend_term_daily_repayments", nullable = false)
private Boolean extendTermForDailyRepayments; private Boolean extendTermForDailyRepayments;
@Column(name = "extend_term_holiday_repayment", nullable = false)
private Boolean extendTermForRepaymentsOnHolidays;
protected WorkingDays() { protected WorkingDays() {
} }
protected WorkingDays(final String recurrence, final Integer repaymentReschedulingType, final Boolean extendTermForDailyRepayments ) { protected WorkingDays(final String recurrence, final Integer repaymentReschedulingType, final Boolean extendTermForDailyRepayments, final Boolean extendTermForRepaymentsOnHolidays) {
this.recurrence = recurrence; this.recurrence = recurrence;
this.repaymentReschedulingType = repaymentReschedulingType; this.repaymentReschedulingType = repaymentReschedulingType;
this.extendTermForDailyRepayments = extendTermForDailyRepayments; this.extendTermForDailyRepayments = extendTermForDailyRepayments;
this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays;
} }
/** /**
@@ -74,6 +78,8 @@ public class WorkingDays extends AbstractPersistable<Long> {
return this.extendTermForDailyRepayments; return this.extendTermForDailyRepayments;
} }
public Boolean getExtendTermForRepaymentsOnHolidays() { return this.extendTermForRepaymentsOnHolidays; }
public Map<String, Object> update(final JsonCommand command) { public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(7); final Map<String, Object> actualChanges = new LinkedHashMap<>(7);
@@ -96,6 +102,13 @@ public class WorkingDays extends AbstractPersistable<Long> {
actualChanges.put(WorkingDaysApiConstants.extendTermForDailyRepayments, newValue); actualChanges.put(WorkingDaysApiConstants.extendTermForDailyRepayments, newValue);
this.extendTermForDailyRepayments = newValue; this.extendTermForDailyRepayments = newValue;
} }
if (command.isChangeInBooleanParameterNamed(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays, this.extendTermForRepaymentsOnHolidays)) {
final Boolean newValue = command.booleanPrimitiveValueOfParameterNamed(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays);
actualChanges.put(WorkingDaysApiConstants.extendTermForRepaymentsOnHolidays, newValue);
this.extendTermForRepaymentsOnHolidays = newValue;
}
return actualChanges; return actualChanges;
} }
@@ -53,7 +53,8 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo
public WorkingDaysMapper() { public WorkingDaysMapper() {
final StringBuilder sqlBuilder = new StringBuilder(100); final StringBuilder sqlBuilder = new StringBuilder(100);
sqlBuilder.append("w.id as id,w.recurrence as recurrence,w.repayment_rescheduling_enum as status_enum,"); sqlBuilder.append("w.id as id,w.recurrence as recurrence,w.repayment_rescheduling_enum as status_enum,");
sqlBuilder.append("w.extend_term_daily_repayments as extendTermForDailyRepayments "); sqlBuilder.append("w.extend_term_daily_repayments as extendTermForDailyRepayments,");
sqlBuilder.append("w.extend_term_holiday_repayment as extendTermForRepaymentsOnHolidays ");
sqlBuilder.append("from m_working_days w"); sqlBuilder.append("from m_working_days w");
this.schema = sqlBuilder.toString(); this.schema = sqlBuilder.toString();
@@ -70,8 +71,9 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo
final Integer statusEnum = JdbcSupport.getInteger(rs, "status_enum"); final Integer statusEnum = JdbcSupport.getInteger(rs, "status_enum");
final EnumOptionData status = WorkingDaysEnumerations.workingDaysStatusType(statusEnum); final EnumOptionData status = WorkingDaysEnumerations.workingDaysStatusType(statusEnum);
final Boolean extendTermForDailyRepayments = rs.getBoolean("extendTermForDailyRepayments"); final Boolean extendTermForDailyRepayments = rs.getBoolean("extendTermForDailyRepayments");
final Boolean extendTermForRepaymentsOnHolidays = rs.getBoolean("extendTermForRepaymentsOnHolidays");
return new WorkingDaysData(id, recurrence, status,extendTermForDailyRepayments); return new WorkingDaysData(id, recurrence, status, extendTermForDailyRepayments, extendTermForRepaymentsOnHolidays);
} }
} }
@@ -93,6 +95,6 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo
WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY), WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY),
WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY), WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY),
WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_PREVIOUS_WORKING_DAY)); WorkingDaysEnumerations.repaymentRescheduleType(RepaymentRescheduleType.MOVE_TO_PREVIOUS_WORKING_DAY));
return new WorkingDaysData(null, null, null, repaymentRescheduleOptions, null); return new WorkingDaysData(null, null, null, repaymentRescheduleOptions, null, null);
} }
} }
@@ -18,9 +18,10 @@
*/ */
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain; package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.holiday.service.HolidayUtil; import org.apache.fineract.organisation.holiday.service.HolidayUtil;
import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType; import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil; import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper; import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
import org.apache.fineract.portfolio.calendar.domain.Calendar; import org.apache.fineract.portfolio.calendar.domain.Calendar;
@@ -29,11 +30,10 @@ import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.DayOfWeekType; import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.joda.time.Days; import org.joda.time.*;
import org.joda.time.LocalDate;
import org.joda.time.Months; import java.util.ArrayList;
import org.joda.time.Weeks; import java.util.List;
import org.joda.time.Years;
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
@@ -57,10 +57,10 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
boolean isFirstRepayment, final HolidayDetailDTO holidayDetailDTO) { boolean isFirstRepayment, final HolidayDetailDTO holidayDetailDTO) {
final LocalDate firstRepaymentPeriodDate = loanApplicationTerms.getCalculatedRepaymentsStartingFromLocalDate(); final LocalDate firstRepaymentPeriodDate = loanApplicationTerms.getCalculatedRepaymentsStartingFromLocalDate();
LocalDate dueRepaymentPeriodDate = null; LocalDate dueRepaymentPeriodDate = null;
Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
if (isFirstRepayment && firstRepaymentPeriodDate != null) { if (isFirstRepayment && firstRepaymentPeriodDate != null) {
dueRepaymentPeriodDate = firstRepaymentPeriodDate; dueRepaymentPeriodDate = firstRepaymentPeriodDate;
} else { } else {
Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(), dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate, null, loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate, null,
null); null);
@@ -96,10 +96,33 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
loanApplicationTerms.getNumberOfdays()); loanApplicationTerms.getNumberOfdays());
} }
} }
if (currentCalendar == null && holidayDetailDTO.getWorkingDays().getExtendTermForRepaymentsOnHolidays()) {
boolean repaymentDateIsOnHoliday = false;
// compile the list of holidays into Intervals to see if this date lands on a holiday
final List<Interval> holidayInterval = new ArrayList<>();
for (Holiday holiday : holidayDetailDTO.getHolidays()) {
holidayInterval.add(new Interval(
holiday.getFromDateLocalDate().toDateTimeAtStartOfDay(),
holiday.getToDateLocalDate().toDateTime(LocalTime.parse("23:59:59"))
));
}
while (intervalListContainsDate(holidayInterval, dueRepaymentPeriodDate)) {
LocalDate previousDate = dueRepaymentPeriodDate;
dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
loanApplicationTerms.getRepaymentEvery(), dueRepaymentPeriodDate, loanApplicationTerms.getNthDay(),
loanApplicationTerms.getWeekDayType());
}
}
return dueRepaymentPeriodDate; return dueRepaymentPeriodDate;
} }
private boolean intervalListContainsDate(List<Interval> intervalList, LocalDate date) {
for (Interval interval : intervalList) {
if (interval.contains(date.toDateTime(LocalTime.parse("11:59:59")))) {
return true;
}
}
return false;
}
@Override @Override
public LocalDate adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate, final LoanApplicationTerms loanApplicationTerms, public LocalDate adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate, final LoanApplicationTerms loanApplicationTerms,
final HolidayDetailDTO holidayDetailDTO) { final HolidayDetailDTO holidayDetailDTO) {
@@ -0,0 +1,20 @@
--
-- 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.
--
ALTER TABLE m_working_days ADD extend_term_holiday_repayment BOOLEAN NOT NULL DEFAULT 0;