From b478064ac0fe74a7130b9a0c2f07a5ccd30631a5 Mon Sep 17 00:00:00 2001 From: Terence Denzil Monteiro Date: Thu, 25 Aug 2016 17:45:14 +0530 Subject: [PATCH] Reschedule repayment on holidays+extend term --- .../api/WorkingDaysApiConstants.java | 6 +-- .../workingdays/data/WorkingDayValidator.java | 6 ++- .../workingdays/data/WorkingDaysData.java | 11 +++-- .../workingdays/domain/WorkingDays.java | 15 ++++++- .../WorkingDaysReadPlatformServiceImpl.java | 8 ++-- .../domain/DefaultScheduledDateGenerator.java | 41 +++++++++++++++---- .../V320__add_holiday_payment_reschedule.sql | 20 +++++++++ 7 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V320__add_holiday_payment_reschedule.sql diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiConstants.java index 5eebbe97d..fda39bbba 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/api/WorkingDaysApiConstants.java @@ -36,14 +36,14 @@ public class WorkingDaysApiConstants { public static final String rescheduleRepaymentTemplate = "rescheduleRepaymentTemplate"; public static final String localeParamName = "locale"; public static final String extendTermForDailyRepayments = "extendTermForDailyRepayments"; - + public static final String extendTermForRepaymentsOnHolidays = "extendTermForRepaymentsOnHolidays"; public static final Set 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 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 WORKING_DAYS_TEMPLATE_PARAMETERS = new HashSet<>(Arrays.asList(rescheduleRepaymentTemplate)); diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java index 271a5a6e3..466ca0ff3 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDayValidator.java @@ -67,14 +67,16 @@ public class WorkingDayValidator { final Boolean extendTermForDailyRepayments = this.fromApiJsonHelper.extractBooleanNamed("extendTermForDailyRepayments", element); 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); } private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { if (!dataValidationErrors.isEmpty()) { - // throw new PlatformApiDataValidationException(dataValidationErrors); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java index affdc035f..72fd6d074 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/data/WorkingDaysData.java @@ -31,28 +31,33 @@ public class WorkingDaysData { @SuppressWarnings("unused") private final EnumOptionData repaymentRescheduleType; - + @SuppressWarnings("unused") private final Boolean extendTermForDailyRepayments; + @SuppressWarnings("unused") + private final Boolean extendTermForRepaymentsOnHolidays; + // template date @SuppressWarnings("unused") private final Collection 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.recurrence = recurrence; this.repaymentRescheduleType = repaymentRescheduleType; this.repaymentRescheduleOptions = null; this.extendTermForDailyRepayments = extendTermForDailyRepayments; + this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays; } public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, - Collection repaymentRescheduleOptions, Boolean extendTermForDailyRepayments) { + Collection repaymentRescheduleOptions, Boolean extendTermForDailyRepayments, Boolean extendTermForRepaymentsOnHolidays) { this.id = id; this.recurrence = recurrence; this.repaymentRescheduleType = repaymentRescheduleType; this.repaymentRescheduleOptions = repaymentRescheduleOptions; this.extendTermForDailyRepayments = extendTermForDailyRepayments; + this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java index adeba4a19..75852c722 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDays.java @@ -41,15 +41,19 @@ public class WorkingDays extends AbstractPersistable { @Column(name = "extend_term_daily_repayments", nullable = false) private Boolean extendTermForDailyRepayments; + + @Column(name = "extend_term_holiday_repayment", nullable = false) + private Boolean extendTermForRepaymentsOnHolidays; 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.repaymentReschedulingType = repaymentReschedulingType; this.extendTermForDailyRepayments = extendTermForDailyRepayments; + this.extendTermForRepaymentsOnHolidays = extendTermForRepaymentsOnHolidays; } /** @@ -74,6 +78,8 @@ public class WorkingDays extends AbstractPersistable { return this.extendTermForDailyRepayments; } + public Boolean getExtendTermForRepaymentsOnHolidays() { return this.extendTermForRepaymentsOnHolidays; } + public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap<>(7); @@ -96,6 +102,13 @@ public class WorkingDays extends AbstractPersistable { actualChanges.put(WorkingDaysApiConstants.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; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java index 085e42ebc..8c4153cd5 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java @@ -53,7 +53,8 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo public WorkingDaysMapper() { 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.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"); this.schema = sqlBuilder.toString(); @@ -70,8 +71,9 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo final Integer statusEnum = JdbcSupport.getInteger(rs, "status_enum"); final EnumOptionData status = WorkingDaysEnumerations.workingDaysStatusType(statusEnum); 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_REPAYMENT_MEETING_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); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java index b57f215e1..93a58ce8e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java @@ -18,9 +18,10 @@ */ 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.workingdays.domain.RepaymentRescheduleType; +import org.apache.fineract.organisation.workingdays.domain.WorkingDays; import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil; import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper; 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.PeriodFrequencyType; import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; -import org.joda.time.Days; -import org.joda.time.LocalDate; -import org.joda.time.Months; -import org.joda.time.Weeks; -import org.joda.time.Years; +import org.joda.time.*; + +import java.util.ArrayList; +import java.util.List; public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { @@ -57,10 +57,10 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { boolean isFirstRepayment, final HolidayDetailDTO holidayDetailDTO) { final LocalDate firstRepaymentPeriodDate = loanApplicationTerms.getCalculatedRepaymentsStartingFromLocalDate(); LocalDate dueRepaymentPeriodDate = null; + Calendar currentCalendar = loanApplicationTerms.getLoanCalendar(); if (isFirstRepayment && firstRepaymentPeriodDate != null) { dueRepaymentPeriodDate = firstRepaymentPeriodDate; } else { - Calendar currentCalendar = loanApplicationTerms.getLoanCalendar(); dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate, null, null); @@ -96,10 +96,33 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { 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 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; } - + private boolean intervalListContainsDate(List intervalList, LocalDate date) { + for (Interval interval : intervalList) { + if (interval.contains(date.toDateTime(LocalTime.parse("11:59:59")))) { + return true; + } + } + return false; + } @Override public LocalDate adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate, final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) { diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V320__add_holiday_payment_reschedule.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V320__add_holiday_payment_reschedule.sql new file mode 100644 index 000000000..0b25e00ca --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V320__add_holiday_payment_reschedule.sql @@ -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;