diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 5095ce040..5f46ccd01 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -13971,6 +13971,12 @@ Request Body: repayment reschedule type .Options listed on the schedule for repayments on non-working days + + extendTermForDailyRepayments + + + Extend the term for loans following a daily repayment schedule.Expects boolean value true/false + locale @@ -14080,6 +14086,7 @@ GET https://DomainName/api/v1/workingdays "code": "RepaymentRescheduleType.move.to.next.working.day", "value": "move to next working day" } + "extendTermForDailyRepayments" : true } ] @@ -14094,7 +14101,7 @@ GET https://DomainName/api/v1/workingdays
Mandatory Fields
- recurrence,repaymentRescheduleType,locale + recurrence,repaymentRescheduleType,extendTermForDailyRepayments,locale @@ -14109,7 +14116,8 @@ Request Body: { "recurrence": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,SU", "locale": "en", -"repaymentsRescheduledType": 4 +"repaymentsRescheduledType": 4, +"extendTermForDailyRepayments": false } diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/WorkingDaysHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/WorkingDaysHelper.java index 22ed88aa8..af2558c5b 100755 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/WorkingDaysHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/WorkingDaysHelper.java @@ -36,6 +36,7 @@ public class WorkingDaysHelper { map.put("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR,SA,SU"); map.put("locale", "en"); map.put("repaymentRescheduleType", randomInt(1, 4)); + map.put("extendTermForDailyRepayments", false); System.out.println("map : " + map); return new Gson().toJson(map); } @@ -45,6 +46,7 @@ public class WorkingDaysHelper { map.put("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MP,TI,TE,TH"); map.put("locale", "en"); map.put("repaymentRescheduleType", randomInt(1, 4)); + map.put("extendTermForDailyRepayments", false); System.out.println("map : " + map); return new Gson().toJson(map); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiConstants.java index f18164bac..187085740 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/api/WorkingDaysApiConstants.java @@ -22,14 +22,15 @@ public class WorkingDaysApiConstants { public static final String rescheduleRepaymentTemplate = "rescheduleRepaymentTemplate"; public static final String localeParamName = "locale"; + public static final String extendTermForDailyRepayments = "extendTermForDailyRepayments"; public static final Set WORKING_DAYS_CREATE_OR_UPDATE_REQUEST_DATA_PARAMETERS =new HashSet<>(Arrays.asList( - recurrence,repayment_rescheduling_enum,localeParamName + recurrence,repayment_rescheduling_enum,localeParamName,extendTermForDailyRepayments )); public static final Set WORKING_DAYS_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName, - recurrence,repayment_rescheduling_enum + recurrence,repayment_rescheduling_enum,extendTermForDailyRepayments )); public static final Set WORKING_DAYS_TEMPLATE_PARAMETERS = new HashSet<>(Arrays.asList(rescheduleRepaymentTemplate)); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDayValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDayValidator.java index 2d40abb1f..44ae0ad65 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDayValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDayValidator.java @@ -52,6 +52,9 @@ public class WorkingDayValidator { final Integer repaymentRescheduleType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("repaymentRescheduleType", element); baseDataValidator.reset().parameter("repaymentRescheduleType").value(repaymentRescheduleType).ignoreIfNull().inMinMaxRange(1, 4); + final Boolean extendTermForDailyRepayments = this.fromApiJsonHelper.extractBooleanNamed("extendTermForDailyRepayments", element); + baseDataValidator.reset().parameter(WorkingDaysApiConstants.extendTermForDailyRepayments).value(extendTermForDailyRepayments).ignoreIfNull().validateForBooleanValue(); + throwExceptionIfValidationWarningsExist(dataValidationErrors); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDaysData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDaysData.java index c9063805a..b1551def0 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDaysData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/data/WorkingDaysData.java @@ -18,23 +18,28 @@ public class WorkingDaysData { @SuppressWarnings("unused") private final EnumOptionData repaymentRescheduleType; + + @SuppressWarnings("unused") + private final Boolean extendTermForDailyRepayments; // template date @SuppressWarnings("unused") private final Collection repaymentRescheduleOptions; - public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType) { + public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, Boolean extendTermForDailyRepayments) { this.id = id; this.recurrence = recurrence; this.repaymentRescheduleType = repaymentRescheduleType; this.repaymentRescheduleOptions = null; + this.extendTermForDailyRepayments = extendTermForDailyRepayments; } public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, - Collection repaymentRescheduleOptions) { + Collection repaymentRescheduleOptions, Boolean extendTermForDailyRepayments) { this.id = id; this.recurrence = recurrence; this.repaymentRescheduleType = repaymentRescheduleType; this.repaymentRescheduleOptions = repaymentRescheduleOptions; + this.extendTermForDailyRepayments = extendTermForDailyRepayments; } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java index 4e2383952..f2d11f3a8 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/domain/WorkingDays.java @@ -7,11 +7,13 @@ package org.mifosplatform.organisation.workingdays.domain; import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.organisation.workingdays.api.WorkingDaysApiConstants; import org.springframework.data.jpa.domain.AbstractPersistable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; + import java.util.LinkedHashMap; import java.util.Map; @@ -25,13 +27,17 @@ public class WorkingDays extends AbstractPersistable { @Column(name = "repayment_rescheduling_enum", nullable = false) private Integer repaymentReschedulingType; + @Column(name = "extend_term_daily_repayments", nullable = false) + private Boolean extendTermForDailyRepayments; + protected WorkingDays() { } - protected WorkingDays(final String recurrence, final Integer repaymentReschedulingType) { + protected WorkingDays(final String recurrence, final Integer repaymentReschedulingType, final Boolean extendTermForDailyRepayments ) { this.recurrence = recurrence; this.repaymentReschedulingType = repaymentReschedulingType; + this.extendTermForDailyRepayments = extendTermForDailyRepayments; } /** @@ -48,6 +54,13 @@ public class WorkingDays extends AbstractPersistable { return this.repaymentReschedulingType; } + public void setRepaymentReschedulingType(Integer repaymentReschedulingType) { + this.repaymentReschedulingType = repaymentReschedulingType; + } + + public Boolean getExtendTermForDailyRepayments(){ + return this.extendTermForDailyRepayments; + } public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap<>(7); @@ -65,6 +78,12 @@ public class WorkingDays extends AbstractPersistable { actualChanges.put(repaymentRescheduleTypeParamName, WorkingDaysEnumerations.workingDaysStatusType(newValue)); this.repaymentReschedulingType = RepaymentRescheduleType.fromInt(newValue).getValue(); } + + if(command.isChangeInBooleanParameterNamed(WorkingDaysApiConstants.extendTermForDailyRepayments, this.extendTermForDailyRepayments)){ + final Boolean newValue = command.booleanPrimitiveValueOfParameterNamed(WorkingDaysApiConstants.extendTermForDailyRepayments); + actualChanges.put(WorkingDaysApiConstants.extendTermForDailyRepayments, newValue); + this.extendTermForDailyRepayments = newValue; + } return actualChanges; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java index 4c0d89441..3a4d65822 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/workingdays/service/WorkingDaysReadPlatformServiceImpl.java @@ -39,7 +39,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.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("from m_working_days w"); this.schema = sqlBuilder.toString(); @@ -55,8 +56,9 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo final String recurrence = rs.getString("recurrence"); final Integer statusEnum = JdbcSupport.getInteger(rs, "status_enum"); final EnumOptionData status = WorkingDaysEnumerations.workingDaysStatusType(statusEnum); + final Boolean extendTermForDailyRepayments = rs.getBoolean("extendTermForDailyRepayments"); - return new WorkingDaysData(id, recurrence, status); + return new WorkingDaysData(id, recurrence, status,extendTermForDailyRepayments); } } @@ -78,6 +80,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); + return new WorkingDaysData(null, null, null, repaymentRescheduleOptions, null); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index 972d3f07e..e0b9c7b7e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -23,8 +23,10 @@ import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.workingdays.domain.RepaymentRescheduleType; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.calendar.service.CalendarUtils; +import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO; import org.mifosplatform.portfolio.loanaccount.domain.Loan; @@ -145,14 +147,25 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final Map latePaymentMap = new HashMap<>(); final List installments = new ArrayList<>(); LocalDate currentDate = DateUtils.getLocalDateOfTenant(); + Boolean extendTermForDailyRepayments = false; + if(holidayDetailDTO.getWorkingDays().getExtendTermForDailyRepayments() == true && + loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.DAYS && + loanApplicationTerms.getRepaymentEvery() == 1){ + holidayDetailDTO.getWorkingDays().setRepaymentReschedulingType(RepaymentRescheduleType.MOVE_TO_NEXT_WORKING_DAY.getValue()); + extendTermForDailyRepayments = true; + } while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) { actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, isFirstRepayment); - isFirstRepayment = false; + isFirstRepayment = false; LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms, holidayDetailDTO); + if(extendTermForDailyRepayments){ + actualRepaymentDate = scheduledDueDate; + } + // calculated interest start date for the period periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java index 7623763ef..caf5f793f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java @@ -11,6 +11,7 @@ import org.joda.time.Months; import org.joda.time.Weeks; import org.joda.time.Years; import org.mifosplatform.organisation.holiday.service.HolidayUtil; +import org.mifosplatform.organisation.workingdays.domain.RepaymentRescheduleType; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.mifosplatform.portfolio.common.domain.DayOfWeekType; import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; @@ -53,22 +54,32 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { final HolidayDetailDTO holidayDetailDTO) { LocalDate adjustedDate = dueRepaymentPeriodDate; + + LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(), + loanApplicationTerms.getRepaymentEvery(), adjustedDate, loanApplicationTerms.getNthDay(), + loanApplicationTerms.getWeekDayType()); + + final RepaymentRescheduleType rescheduleType = RepaymentRescheduleType.fromInt(holidayDetailDTO.getWorkingDays().getRepaymentReschedulingType()); + /** * Fix for https://mifosforge.jira.com/browse/MIFOSX-1357 */ - // recursively check for the next working day. - while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDate)) { + // recursively check for the next working meeting day. + while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), nextDueRepaymentPeriodDate) && + rescheduleType == RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY) { - final LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(), - loanApplicationTerms.getRepaymentEvery(), adjustedDate, loanApplicationTerms.getNthDay(), + nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(), + loanApplicationTerms.getRepaymentEvery(), nextDueRepaymentPeriodDate, loanApplicationTerms.getNthDay(), loanApplicationTerms.getWeekDayType()); - adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate, - holidayDetailDTO.getWorkingDays()); + } + adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate, + holidayDetailDTO.getWorkingDays()); if (holidayDetailDTO.isHolidayEnabled()) { adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidayDetailDTO.getHolidays()); } + return adjustedDate; } diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V258__alter_working_days.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V258__alter_working_days.sql new file mode 100644 index 000000000..223d527d0 --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V258__alter_working_days.sql @@ -0,0 +1 @@ +ALTER TABLE `m_working_days` ADD extend_term_daily_repayments TINYINT(1) NULL DEFAULT '0'; \ No newline at end of file