MIFOSX-1891 Improvements to extend term for loans following daily repayment schedule.

This commit is contained in:
Chandrika
2015-04-21 17:01:39 -04:00
parent 3583fabe7a
commit 82d3d692d7
10 changed files with 82 additions and 17 deletions
+10 -2
View File
@@ -13971,6 +13971,12 @@ Request Body:
<td class=fielddesc>repayment reschedule type .Options listed on the schedule for repayments on non-working days <td class=fielddesc>repayment reschedule type .Options listed on the schedule for repayments on non-working days
</td> </td>
</tr> </tr>
<tr class=alt>
<td>extendTermForDailyRepayments</td>
</tr>
<tr>
<td class=fielddesc>Extend the term for loans following a daily repayment schedule.Expects boolean value true/false</td>
</tr>
<tr class=alt> <tr class=alt>
<td>locale</td> <td>locale</td>
</tr> </tr>
@@ -14080,6 +14086,7 @@ GET https://DomainName/api/v1/workingdays
"code": "RepaymentRescheduleType.move.to.next.working.day", "code": "RepaymentRescheduleType.move.to.next.working.day",
"value": "move to next working day" "value": "move to next working day"
} }
"extendTermForDailyRepayments" : true
} }
] ]
</code> </code>
@@ -14094,7 +14101,7 @@ GET https://DomainName/api/v1/workingdays
<td><div class="mifosXHeading2">Mandatory Fields</div></td> <td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr> </tr>
<tr class=alt> <tr class=alt>
<td>recurrence,repaymentRescheduleType,locale</td> <td>recurrence,repaymentRescheduleType,extendTermForDailyRepayments,locale</td>
</tr> </tr>
</table> </table>
</div> </div>
@@ -14109,7 +14116,8 @@ Request Body:
{ {
"recurrence": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,SU", "recurrence": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,SU",
"locale": "en", "locale": "en",
"repaymentsRescheduledType": 4 "repaymentsRescheduledType": 4,
"extendTermForDailyRepayments": false
} }
</code> </code>
<code class="method-response"> <code class="method-response">
@@ -36,6 +36,7 @@ public class WorkingDaysHelper {
map.put("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR,SA,SU"); map.put("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR,SA,SU");
map.put("locale", "en"); map.put("locale", "en");
map.put("repaymentRescheduleType", randomInt(1, 4)); map.put("repaymentRescheduleType", randomInt(1, 4));
map.put("extendTermForDailyRepayments", false);
System.out.println("map : " + map); System.out.println("map : " + map);
return new Gson().toJson(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("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MP,TI,TE,TH");
map.put("locale", "en"); map.put("locale", "en");
map.put("repaymentRescheduleType", randomInt(1, 4)); map.put("repaymentRescheduleType", randomInt(1, 4));
map.put("extendTermForDailyRepayments", false);
System.out.println("map : " + map); System.out.println("map : " + map);
return new Gson().toJson(map); return new Gson().toJson(map);
} }
@@ -22,14 +22,15 @@ 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 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 recurrence,repayment_rescheduling_enum,localeParamName,extendTermForDailyRepayments
)); ));
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 recurrence,repayment_rescheduling_enum,extendTermForDailyRepayments
)); ));
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));
@@ -52,6 +52,9 @@ public class WorkingDayValidator {
final Integer repaymentRescheduleType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("repaymentRescheduleType", element); final Integer repaymentRescheduleType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("repaymentRescheduleType", element);
baseDataValidator.reset().parameter("repaymentRescheduleType").value(repaymentRescheduleType).ignoreIfNull().inMinMaxRange(1, 4); 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); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -18,23 +18,28 @@ public class WorkingDaysData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final EnumOptionData repaymentRescheduleType; private final EnumOptionData repaymentRescheduleType;
@SuppressWarnings("unused")
private final Boolean extendTermForDailyRepayments;
// 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) { public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, Boolean extendTermForDailyRepayments) {
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;
} }
public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType, public WorkingDaysData(Long id, String recurrence, EnumOptionData repaymentRescheduleType,
Collection<EnumOptionData> repaymentRescheduleOptions) { Collection<EnumOptionData> repaymentRescheduleOptions, Boolean extendTermForDailyRepayments) {
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;
} }
} }
@@ -7,11 +7,13 @@
package org.mifosplatform.organisation.workingdays.domain; package org.mifosplatform.organisation.workingdays.domain;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.workingdays.api.WorkingDaysApiConstants;
import org.springframework.data.jpa.domain.AbstractPersistable; import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@@ -25,13 +27,17 @@ public class WorkingDays extends AbstractPersistable<Long> {
@Column(name = "repayment_rescheduling_enum", nullable = false) @Column(name = "repayment_rescheduling_enum", nullable = false)
private Integer repaymentReschedulingType; private Integer repaymentReschedulingType;
@Column(name = "extend_term_daily_repayments", nullable = false)
private Boolean extendTermForDailyRepayments;
protected WorkingDays() { 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.recurrence = recurrence;
this.repaymentReschedulingType = repaymentReschedulingType; this.repaymentReschedulingType = repaymentReschedulingType;
this.extendTermForDailyRepayments = extendTermForDailyRepayments;
} }
/** /**
@@ -48,6 +54,13 @@ public class WorkingDays extends AbstractPersistable<Long> {
return this.repaymentReschedulingType; return this.repaymentReschedulingType;
} }
public void setRepaymentReschedulingType(Integer repaymentReschedulingType) {
this.repaymentReschedulingType = repaymentReschedulingType;
}
public Boolean getExtendTermForDailyRepayments(){
return this.extendTermForDailyRepayments;
}
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);
@@ -65,6 +78,12 @@ public class WorkingDays extends AbstractPersistable<Long> {
actualChanges.put(repaymentRescheduleTypeParamName, WorkingDaysEnumerations.workingDaysStatusType(newValue)); actualChanges.put(repaymentRescheduleTypeParamName, WorkingDaysEnumerations.workingDaysStatusType(newValue));
this.repaymentReschedulingType = RepaymentRescheduleType.fromInt(newValue).getValue(); 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; return actualChanges;
} }
@@ -39,7 +39,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("from m_working_days w"); sqlBuilder.append("from m_working_days w");
this.schema = sqlBuilder.toString(); this.schema = sqlBuilder.toString();
@@ -55,8 +56,9 @@ public class WorkingDaysReadPlatformServiceImpl implements WorkingDaysReadPlatfo
final String recurrence = rs.getString("recurrence"); final String recurrence = rs.getString("recurrence");
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");
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_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); return new WorkingDaysData(null, null, null, repaymentRescheduleOptions, null);
} }
} }
@@ -23,8 +23,10 @@ import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money; 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.domain.CalendarInstance;
import org.mifosplatform.portfolio.calendar.service.CalendarUtils; 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.DisbursementData;
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO; import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.Loan;
@@ -145,14 +147,25 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
final Map<LocalDate, Money> latePaymentMap = new HashMap<>(); final Map<LocalDate, Money> latePaymentMap = new HashMap<>();
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>(); final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
LocalDate currentDate = DateUtils.getLocalDateOfTenant(); 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()) { while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) {
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms, actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
isFirstRepayment); isFirstRepayment);
isFirstRepayment = false; isFirstRepayment = false;
LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms, LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
holidayDetailDTO); holidayDetailDTO);
if(extendTermForDailyRepayments){
actualRepaymentDate = scheduledDueDate;
}
// calculated interest start date for the period // calculated interest start date for the period
periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, periodStartDate, periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, periodStartDate,
idealDisbursementDate, periodStartDateApplicableForInterest); idealDisbursementDate, periodStartDateApplicableForInterest);
@@ -11,6 +11,7 @@ import org.joda.time.Months;
import org.joda.time.Weeks; import org.joda.time.Weeks;
import org.joda.time.Years; import org.joda.time.Years;
import org.mifosplatform.organisation.holiday.service.HolidayUtil; import org.mifosplatform.organisation.holiday.service.HolidayUtil;
import org.mifosplatform.organisation.workingdays.domain.RepaymentRescheduleType;
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
import org.mifosplatform.portfolio.common.domain.DayOfWeekType; import org.mifosplatform.portfolio.common.domain.DayOfWeekType;
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
@@ -53,22 +54,32 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
final HolidayDetailDTO holidayDetailDTO) { final HolidayDetailDTO holidayDetailDTO) {
LocalDate adjustedDate = dueRepaymentPeriodDate; 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 * Fix for https://mifosforge.jira.com/browse/MIFOSX-1357
*/ */
// recursively check for the next working day. // recursively check for the next working meeting day.
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDate)) { while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), nextDueRepaymentPeriodDate) &&
rescheduleType == RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY) {
final LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(), nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
loanApplicationTerms.getRepaymentEvery(), adjustedDate, loanApplicationTerms.getNthDay(), loanApplicationTerms.getRepaymentEvery(), nextDueRepaymentPeriodDate, loanApplicationTerms.getNthDay(),
loanApplicationTerms.getWeekDayType()); loanApplicationTerms.getWeekDayType());
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate,
holidayDetailDTO.getWorkingDays());
} }
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate,
holidayDetailDTO.getWorkingDays());
if (holidayDetailDTO.isHolidayEnabled()) { if (holidayDetailDTO.isHolidayEnabled()) {
adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidayDetailDTO.getHolidays()); adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidayDetailDTO.getHolidays());
} }
return adjustedDate; return adjustedDate;
} }
@@ -0,0 +1 @@
ALTER TABLE `m_working_days` ADD extend_term_daily_repayments TINYINT(1) NULL DEFAULT '0';