Merge branch 'FINERACT-37' into develop
This commit is contained in:
+1
@@ -41,6 +41,7 @@ public class HolidayApiConstants {
|
||||
public static final String repaymentsRescheduledToParamName = "repaymentsRescheduledTo";
|
||||
public static final String processed = "processed";
|
||||
public static final String status = "status";
|
||||
public static final String reschedulingType = "reschedulingType";
|
||||
|
||||
protected static final Set<String> HOLIDAY_RESPONSE_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList(idParamName, nameParamName, fromDateParamName, descriptionParamName, toDateParamName,
|
||||
|
||||
+9
@@ -180,4 +180,13 @@ public class HolidaysApiResource {
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, holidays, HOLIDAY_RESPONSE_DATA_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/template")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public String retrieveRepaymentScheduleUpdationTyeOptions(@Context final UriInfo uriInfo){
|
||||
this.context.authenticatedUser().validateHasReadPermission(HOLIDAY_RESOURCE_NAME);
|
||||
return this.toApiJsonSerializer.serialize(this.holidayReadPlatformService.retrieveRepaymentScheduleUpdationTyeOptions());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -39,9 +39,10 @@ public class HolidayData {
|
||||
private final Long officeId;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData status;
|
||||
private final Integer reschedulingType;
|
||||
|
||||
public HolidayData(final Long id, final String name, final String description, final LocalDate fromDate, final LocalDate toDate,
|
||||
final LocalDate repaymentsRescheduledTo, final EnumOptionData status) {
|
||||
final LocalDate repaymentsRescheduledTo, final EnumOptionData status, final Integer reschedulingType) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
@@ -50,5 +51,6 @@ public class HolidayData {
|
||||
this.repaymentsRescheduledTo = repaymentsRescheduledTo;
|
||||
this.officeId = null;
|
||||
this.status = status;
|
||||
this.reschedulingType = reschedulingType;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-6
@@ -23,6 +23,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -33,6 +34,7 @@ import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.organisation.holiday.api.HolidayApiConstants;
|
||||
import org.apache.fineract.organisation.holiday.domain.RescheduleType;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -50,7 +52,8 @@ public class HolidayDataValidator {
|
||||
Arrays.asList(HolidayApiConstants.localeParamName, HolidayApiConstants.dateFormatParamName,
|
||||
HolidayApiConstants.nameParamName, HolidayApiConstants.fromDateParamName,
|
||||
HolidayApiConstants.toDateParamName, HolidayApiConstants.descriptionParamName,
|
||||
HolidayApiConstants.officesParamName, HolidayApiConstants.repaymentsRescheduledToParamName));
|
||||
HolidayApiConstants.officesParamName, HolidayApiConstants.repaymentsRescheduledToParamName,
|
||||
HolidayApiConstants.reschedulingType));
|
||||
|
||||
@Autowired
|
||||
public HolidayDataValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
@@ -79,11 +82,18 @@ public class HolidayDataValidator {
|
||||
|
||||
final LocalDate toDate = this.fromApiJsonHelper.extractLocalDateNamed(HolidayApiConstants.toDateParamName, element);
|
||||
baseDataValidator.reset().parameter(HolidayApiConstants.toDateParamName).value(toDate).notNull();
|
||||
|
||||
final LocalDate repaymentsRescheduledTo = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||
HolidayApiConstants.repaymentsRescheduledToParamName, element);
|
||||
baseDataValidator.reset().parameter(HolidayApiConstants.repaymentsRescheduledToParamName).value(repaymentsRescheduledTo).notNull();
|
||||
|
||||
|
||||
Integer reschedulingType = null;
|
||||
if(this.fromApiJsonHelper.parameterExists(HolidayApiConstants.reschedulingType, element)){
|
||||
reschedulingType = this.fromApiJsonHelper.extractIntegerNamed(HolidayApiConstants.reschedulingType, element, Locale.ENGLISH);
|
||||
}
|
||||
|
||||
LocalDate repaymentsRescheduledTo = null;
|
||||
if(reschedulingType == null || reschedulingType.equals(RescheduleType.RESCHEDULETOSPECIFICDATE.getValue())){
|
||||
repaymentsRescheduledTo = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||
HolidayApiConstants.repaymentsRescheduledToParamName, element);
|
||||
baseDataValidator.reset().parameter(HolidayApiConstants.repaymentsRescheduledToParamName).value(repaymentsRescheduledTo).notNull();
|
||||
}
|
||||
Set<Long> offices = null;
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
|
||||
|
||||
+31
-5
@@ -73,9 +73,12 @@ public class Holiday extends AbstractPersistableCustom<Long> {
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date toDate;
|
||||
|
||||
@Column(name = "repayments_rescheduled_to", nullable = false)
|
||||
@Column(name = "repayments_rescheduled_to", nullable = true)
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date repaymentsRescheduledTo;
|
||||
|
||||
@Column(name = "rescheduling_type", nullable = false)
|
||||
private int reschedulingType;
|
||||
|
||||
@Column(name = "status_enum", nullable = false)
|
||||
private Integer status;
|
||||
@@ -94,13 +97,21 @@ public class Holiday extends AbstractPersistableCustom<Long> {
|
||||
final String name = command.stringValueOfParameterNamed(HolidayApiConstants.nameParamName);
|
||||
final LocalDate fromDate = command.localDateValueOfParameterNamed(HolidayApiConstants.fromDateParamName);
|
||||
final LocalDate toDate = command.localDateValueOfParameterNamed(HolidayApiConstants.toDateParamName);
|
||||
final LocalDate repaymentsRescheduledTo = command
|
||||
.localDateValueOfParameterNamed(HolidayApiConstants.repaymentsRescheduledToParamName);
|
||||
Integer reschedulingType = null;
|
||||
if(command.parameterExists(HolidayApiConstants.reschedulingType)){
|
||||
reschedulingType = command.integerValueOfParameterNamed(HolidayApiConstants.reschedulingType);
|
||||
}
|
||||
LocalDate repaymentsRescheduledTo = null;
|
||||
if(reschedulingType == null || reschedulingType.equals(RescheduleType.RESCHEDULETOSPECIFICDATE.getValue())){
|
||||
repaymentsRescheduledTo = command
|
||||
.localDateValueOfParameterNamed(HolidayApiConstants.repaymentsRescheduledToParamName);
|
||||
}
|
||||
final Integer status = HolidayStatusType.PENDING_FOR_ACTIVATION.getValue();
|
||||
final boolean processed = false;// default it to false. Only batch job
|
||||
// should update this field.
|
||||
final String description = command.stringValueOfParameterNamed(HolidayApiConstants.descriptionParamName);
|
||||
return new Holiday(name, fromDate, toDate, repaymentsRescheduledTo, status, processed, description, offices);
|
||||
|
||||
return new Holiday(name, fromDate, toDate, repaymentsRescheduledTo, status, processed, description, offices, reschedulingType);
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
@@ -125,6 +136,16 @@ public class Holiday extends AbstractPersistableCustom<Long> {
|
||||
actualChanges.put(descriptionParamName, newValue);
|
||||
this.description = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(HolidayApiConstants.reschedulingType,this.reschedulingType)) {
|
||||
final Integer newValue =command.integerValueOfParameterNamed(HolidayApiConstants.reschedulingType);
|
||||
actualChanges.put(HolidayApiConstants.reschedulingType, newValue);
|
||||
this.reschedulingType = RescheduleType.fromInt(newValue).getValue();
|
||||
if(newValue.equals(RescheduleType.RESCHEDULETONEXTREPAYMENTDATE.getValue())){
|
||||
this.repaymentsRescheduledTo = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentStatus.isPendingActivation()) {
|
||||
if (command.isChangeInLocalDateParameterNamed(fromDateParamName, getFromDateLocalDate())) {
|
||||
@@ -205,7 +226,7 @@ public class Holiday extends AbstractPersistableCustom<Long> {
|
||||
}
|
||||
|
||||
private Holiday(final String name, final LocalDate fromDate, final LocalDate toDate, final LocalDate repaymentsRescheduledTo,
|
||||
final Integer status, final boolean processed, final String description, final Set<Office> offices) {
|
||||
final Integer status, final boolean processed, final String description, final Set<Office> offices, final int reschedulingType) {
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
this.name = name.trim();
|
||||
}
|
||||
@@ -234,6 +255,7 @@ public class Holiday extends AbstractPersistableCustom<Long> {
|
||||
if (offices != null) {
|
||||
this.offices = offices;
|
||||
}
|
||||
this.reschedulingType = reschedulingType;
|
||||
}
|
||||
|
||||
protected Holiday() {}
|
||||
@@ -298,4 +320,8 @@ public class Holiday extends AbstractPersistableCustom<Long> {
|
||||
}
|
||||
this.status = HolidayStatusType.DELETED.getValue();
|
||||
}
|
||||
|
||||
public RescheduleType getReScheduleType() {
|
||||
return RescheduleType.fromInt(this.reschedulingType);
|
||||
}
|
||||
}
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.organisation.holiday.domain;
|
||||
|
||||
public enum RescheduleType {
|
||||
|
||||
INVALID(0, "rescheduletype.invalid"), RESCHEDULETOSPECIFICDATE(2, "rescheduletype.rescheduletospecificdate"), //
|
||||
RESCHEDULETONEXTREPAYMENTDATE(1, "rescheduletype.rescheduletonextrepaymentdate");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private RescheduleType(Integer value, String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static RescheduleType fromInt(int rescheduleTypeValue) {
|
||||
RescheduleType enumerration = RescheduleType.INVALID;
|
||||
switch (rescheduleTypeValue) {
|
||||
case 1:
|
||||
enumerration = RescheduleType.RESCHEDULETONEXTREPAYMENTDATE;
|
||||
break;
|
||||
case 2:
|
||||
enumerration = RescheduleType.RESCHEDULETOSPECIFICDATE;
|
||||
break;
|
||||
}
|
||||
return enumerration;
|
||||
}
|
||||
|
||||
public boolean isRescheduleToSpecificDate(){
|
||||
return this.value.equals(RescheduleType.RESCHEDULETOSPECIFICDATE.getValue());
|
||||
}
|
||||
|
||||
public boolean isResheduleToNextRepaymentDate(){
|
||||
return this.value.equals(RescheduleType.RESCHEDULETONEXTREPAYMENTDATE.getValue());
|
||||
}
|
||||
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -20,6 +20,7 @@ package org.apache.fineract.organisation.holiday.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayStatusType;
|
||||
import org.apache.fineract.organisation.holiday.domain.RescheduleType;
|
||||
|
||||
public class HolidayEnumerations {
|
||||
|
||||
@@ -49,4 +50,29 @@ public class HolidayEnumerations {
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData rescheduleType(final int id) {
|
||||
return rescheduleType(RescheduleType.fromInt(id));
|
||||
}
|
||||
|
||||
|
||||
public static EnumOptionData rescheduleType(final RescheduleType type) {
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
case RESCHEDULETONEXTREPAYMENTDATE:
|
||||
optionData = new EnumOptionData(RescheduleType.RESCHEDULETONEXTREPAYMENTDATE.getValue().longValue(),
|
||||
RescheduleType.RESCHEDULETONEXTREPAYMENTDATE.getCode(), "Reschedule to next repayment date");
|
||||
break;
|
||||
case RESCHEDULETOSPECIFICDATE:
|
||||
optionData = new EnumOptionData(RescheduleType.RESCHEDULETOSPECIFICDATE.getValue().longValue(),
|
||||
RescheduleType.RESCHEDULETOSPECIFICDATE.getCode(), "Reschedule to specified date");
|
||||
break;
|
||||
|
||||
default:
|
||||
optionData = new EnumOptionData(RescheduleType.INVALID.getValue().longValue(),
|
||||
RescheduleType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -20,7 +20,9 @@ package org.apache.fineract.organisation.holiday.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.holiday.data.HolidayData;
|
||||
|
||||
public interface HolidayReadPlatformService {
|
||||
@@ -28,4 +30,6 @@ public interface HolidayReadPlatformService {
|
||||
Collection<HolidayData> retrieveAllHolidaysBySearchParamerters(final Long officeId, Date fromDate, Date toDate);
|
||||
|
||||
HolidayData retrieveHoliday(final Long holidayId);
|
||||
|
||||
List<EnumOptionData> retrieveRepaymentScheduleUpdationTyeOptions();
|
||||
}
|
||||
|
||||
+14
-2
@@ -25,12 +25,14 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.holiday.data.HolidayData;
|
||||
import org.apache.fineract.organisation.holiday.domain.RescheduleType;
|
||||
import org.apache.fineract.organisation.holiday.exception.HolidayNotFoundException;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -58,7 +60,7 @@ public class HolidayReadPlatformServiceImpl implements HolidayReadPlatformServic
|
||||
public HolidayMapper() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(200);
|
||||
sqlBuilder.append("h.id as id, h.name as name, h.description as description, h.from_date as fromDate, h.to_date as toDate, ");
|
||||
sqlBuilder.append("h.repayments_rescheduled_to as repaymentsScheduleTO, h.status_enum as statusEnum ");
|
||||
sqlBuilder.append("h.repayments_rescheduled_to as repaymentsScheduleTO, h.rescheduling_type as reschedulingType, h.status_enum as statusEnum ");
|
||||
sqlBuilder.append("from m_holiday h ");
|
||||
this.schema = sqlBuilder.toString();
|
||||
}
|
||||
@@ -76,9 +78,10 @@ public class HolidayReadPlatformServiceImpl implements HolidayReadPlatformServic
|
||||
final LocalDate toDate = JdbcSupport.getLocalDate(rs, "toDate");
|
||||
final LocalDate repaymentsScheduleTO = JdbcSupport.getLocalDate(rs, "repaymentsScheduleTO");
|
||||
final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum");
|
||||
final Integer reschedulingType = JdbcSupport.getInteger(rs, "reschedulingType");
|
||||
final EnumOptionData status = HolidayEnumerations.holidayStatusType(statusEnum);
|
||||
|
||||
return new HolidayData(id, name, description, fromDate, toDate, repaymentsScheduleTO, status);
|
||||
return new HolidayData(id, name, description, fromDate, toDate, repaymentsScheduleTO, status, reschedulingType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -133,5 +136,14 @@ public class HolidayReadPlatformServiceImpl implements HolidayReadPlatformServic
|
||||
throw new HolidayNotFoundException(holidayId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveRepaymentScheduleUpdationTyeOptions(){
|
||||
|
||||
final List<EnumOptionData> repSchUpdationTypeOptions = Arrays.asList(
|
||||
HolidayEnumerations.rescheduleType(RescheduleType.RESCHEDULETOSPECIFICDATE),
|
||||
HolidayEnumerations.rescheduleType(RescheduleType.RESCHEDULETONEXTREPAYMENTDATE));
|
||||
return repSchUpdationTypeOptions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
@@ -21,6 +21,7 @@ package org.apache.fineract.organisation.holiday.service;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class HolidayUtil {
|
||||
@@ -56,4 +57,20 @@ public class HolidayUtil {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Holiday getApplicableHoliday(final LocalDate repaymentDate, final List<Holiday> holidays) {
|
||||
Holiday referedHoliday = null;
|
||||
for (final Holiday holiday : holidays) {
|
||||
if (!repaymentDate.isBefore(holiday.getFromDateLocalDate()) && !repaymentDate.isAfter(holiday.getToDateLocalDate())) {
|
||||
referedHoliday = holiday;
|
||||
}
|
||||
}
|
||||
return referedHoliday;
|
||||
}
|
||||
public static void updateRepaymentRescheduleDateToWorkingDayIfItIsHoliday(final AdjustedDateDetailsDTO adjustedDateDetailsDTO,
|
||||
final Holiday holiday) {
|
||||
if (holiday.getReScheduleType().isRescheduleToSpecificDate()) {
|
||||
adjustedDateDetailsDTO.setChangedScheduleDate(holiday.getRepaymentsRescheduledToLocalDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-29
@@ -194,9 +194,18 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit
|
||||
private void validateInputDates(final JsonCommand command) {
|
||||
final LocalDate fromDate = command.localDateValueOfParameterNamed(HolidayApiConstants.fromDateParamName);
|
||||
final LocalDate toDate = command.localDateValueOfParameterNamed(HolidayApiConstants.toDateParamName);
|
||||
final LocalDate repaymentsRescheduledTo = command
|
||||
.localDateValueOfParameterNamed(HolidayApiConstants.repaymentsRescheduledToParamName);
|
||||
this.validateInputDates(fromDate, toDate, repaymentsRescheduledTo);
|
||||
Integer reshedulingType = null;
|
||||
if(command.parameterExists(HolidayApiConstants.reschedulingType)){
|
||||
reshedulingType = command.integerValueOfParameterNamed(HolidayApiConstants.reschedulingType);
|
||||
}
|
||||
LocalDate repaymentsRescheduledTo = null;
|
||||
if(reshedulingType != null && reshedulingType.equals(2)){
|
||||
repaymentsRescheduledTo = command
|
||||
.localDateValueOfParameterNamed(HolidayApiConstants.repaymentsRescheduledToParamName);
|
||||
}
|
||||
if(repaymentsRescheduledTo != null){
|
||||
this.validateInputDates(fromDate, toDate, repaymentsRescheduledTo);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateInputDates(final LocalDate fromDate, final LocalDate toDate, final LocalDate repaymentsRescheduledTo) {
|
||||
@@ -208,37 +217,40 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit
|
||||
throw new HolidayDateException("to.date.cannot.be.before.from.date", defaultUserMessage, fromDate.toString(),
|
||||
toDate.toString());
|
||||
}
|
||||
if(repaymentsRescheduledTo != null){
|
||||
if ((repaymentsRescheduledTo.isEqual(fromDate) || repaymentsRescheduledTo.isEqual(toDate)
|
||||
|| (repaymentsRescheduledTo.isAfter(fromDate) && repaymentsRescheduledTo.isBefore(toDate)))) {
|
||||
|
||||
if (repaymentsRescheduledTo.isEqual(fromDate) || repaymentsRescheduledTo.isEqual(toDate)
|
||||
|| (repaymentsRescheduledTo.isAfter(fromDate) && repaymentsRescheduledTo.isBefore(toDate))) {
|
||||
defaultUserMessage = "Repayments rescheduled date should be before from date or after to date.";
|
||||
throw new HolidayDateException("repayments.rescheduled.date.should.be.before.from.date.or.after.to.date", defaultUserMessage,
|
||||
repaymentsRescheduledTo.toString());
|
||||
}
|
||||
|
||||
defaultUserMessage = "Repayments rescheduled date should be before from date or after to date.";
|
||||
throw new HolidayDateException("repayments.rescheduled.date.should.be.before.from.date.or.after.to.date", defaultUserMessage,
|
||||
repaymentsRescheduledTo.toString());
|
||||
final WorkingDays workingDays = this.daysRepositoryWrapper.findOne();
|
||||
final Boolean isRepaymentOnWorkingDay = WorkingDaysUtil.isWorkingDay(workingDays, repaymentsRescheduledTo);
|
||||
|
||||
if (!isRepaymentOnWorkingDay) {
|
||||
defaultUserMessage = "Repayments rescheduled date should not fall on non working days";
|
||||
throw new HolidayDateException("repayments.rescheduled.date.should.not.fall.on.non.working.day", defaultUserMessage,
|
||||
repaymentsRescheduledTo.toString());
|
||||
}
|
||||
|
||||
// validate repaymentsRescheduledTo date
|
||||
// 1. should be within a 7 days date range.
|
||||
// 2. Alternative date should not be an exist holiday.//TBD
|
||||
// 3. Holiday should not be on an repaymentsRescheduledTo date of
|
||||
// another holiday.//TBD
|
||||
|
||||
// restricting repaymentsRescheduledTo date to be within 7 days range
|
||||
// before or after from date and to date.
|
||||
if (repaymentsRescheduledTo.isBefore(fromDate.minusDays(7)) || repaymentsRescheduledTo.isAfter(toDate.plusDays(7))) {
|
||||
defaultUserMessage = "Repayments Rescheduled to date must be within 7 days before or after from and to dates";
|
||||
throw new HolidayDateException("repayments.rescheduled.to.must.be.within.range", defaultUserMessage, fromDate.toString(),
|
||||
toDate.toString(), repaymentsRescheduledTo.toString());
|
||||
}
|
||||
}
|
||||
|
||||
final WorkingDays workingDays = this.daysRepositoryWrapper.findOne();
|
||||
final Boolean isRepaymentOnWorkingDay = WorkingDaysUtil.isWorkingDay(workingDays, repaymentsRescheduledTo);
|
||||
|
||||
if (!isRepaymentOnWorkingDay) {
|
||||
defaultUserMessage = "Repayments rescheduled date should not fall on non working days";
|
||||
throw new HolidayDateException("repayments.rescheduled.date.should.not.fall.on.non.working.day", defaultUserMessage,
|
||||
repaymentsRescheduledTo.toString());
|
||||
}
|
||||
|
||||
// validate repaymentsRescheduledTo date
|
||||
// 1. should be within a 7 days date range.
|
||||
// 2. Alternative date should not be an exist holiday.//TBD
|
||||
// 3. Holiday should not be on an repaymentsRescheduledTo date of
|
||||
// another holiday.//TBD
|
||||
|
||||
// restricting repaymentsRescheduledTo date to be within 7 days range
|
||||
// before or after from date and to date.
|
||||
if (repaymentsRescheduledTo.isBefore(fromDate.minusDays(7)) || repaymentsRescheduledTo.isAfter(toDate.plusDays(7))) {
|
||||
defaultUserMessage = "Repayments Rescheduled to date must be within 7 days before or after from and to dates";
|
||||
throw new HolidayDateException("repayments.rescheduled.to.must.be.within.range", defaultUserMessage, fromDate.toString(),
|
||||
toDate.toString(), repaymentsRescheduledTo.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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.organisation.workingdays.data;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
|
||||
public class AdjustedDateDetailsDTO {
|
||||
|
||||
/**
|
||||
* Variable tracks the current schedule date that has been changed
|
||||
*/
|
||||
LocalDate changedScheduleDate;
|
||||
/**
|
||||
* Variable tracks If the meeting has been changed , i.e future schedule
|
||||
* also changes along with the current repayments date.
|
||||
*/
|
||||
LocalDate changedActualRepaymentDate;
|
||||
|
||||
/**
|
||||
* Variable tracks the next repayment period due date
|
||||
*/
|
||||
LocalDate nextRepaymentPeriodDueDate;
|
||||
|
||||
public AdjustedDateDetailsDTO(final LocalDate changedScheduleDate, final LocalDate changedActualRepaymentDate) {
|
||||
this.changedScheduleDate = changedScheduleDate;
|
||||
this.changedActualRepaymentDate = changedActualRepaymentDate;
|
||||
}
|
||||
|
||||
public AdjustedDateDetailsDTO(final LocalDate changedScheduleDate, final LocalDate changedActualRepaymentDate,
|
||||
final LocalDate nextRepaymentPeriodDueDate) {
|
||||
this.changedScheduleDate = changedScheduleDate;
|
||||
this.changedActualRepaymentDate = changedActualRepaymentDate;
|
||||
this.nextRepaymentPeriodDueDate = nextRepaymentPeriodDueDate;
|
||||
}
|
||||
|
||||
public LocalDate getChangedScheduleDate() {
|
||||
return this.changedScheduleDate;
|
||||
}
|
||||
|
||||
public LocalDate getChangedActualRepaymentDate() {
|
||||
return this.changedActualRepaymentDate;
|
||||
}
|
||||
|
||||
public void setChangedScheduleDate(final LocalDate changedScheduleDate) {
|
||||
this.changedScheduleDate = changedScheduleDate;
|
||||
}
|
||||
|
||||
public void setChangedActualRepaymentDate(final LocalDate changedActualRepaymentDate) {
|
||||
this.changedActualRepaymentDate = changedActualRepaymentDate;
|
||||
}
|
||||
|
||||
public LocalDate getNextRepaymentPeriodDueDate() {
|
||||
return this.nextRepaymentPeriodDueDate;
|
||||
}
|
||||
|
||||
public void setNextRepaymentPeriodDueDate(final LocalDate nextRepaymentPeriodDueDate) {
|
||||
this.nextRepaymentPeriodDueDate = nextRepaymentPeriodDueDate;
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -26,7 +26,8 @@ public enum RepaymentRescheduleType {
|
||||
INVALID(0, "RepaymentRescheduleType.invalid"), SAME_DAY(1, "RepaymentRescheduleType.same.day"), MOVE_TO_NEXT_WORKING_DAY(2,
|
||||
"RepaymentRescheduleType.move.to.next.working.day"), MOVE_TO_NEXT_REPAYMENT_MEETING_DAY(3,
|
||||
"RepaymentRescheduleType.move.to.next.repayment.meeting.day"), MOVE_TO_PREVIOUS_WORKING_DAY(4,
|
||||
"RepaymentRescheduleType.move.to.previous.working.day");
|
||||
"RepaymentRescheduleType.move.to.previous.working.day"),
|
||||
MOVE_TO_NEXT_MEETING_DAY(5, "RepaymentRescheduleType.move.to.next.meeting.day");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -43,6 +44,10 @@ public enum RepaymentRescheduleType {
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public boolean isMoveToNextRepaymentDay() {
|
||||
return this.value.equals(RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY.getValue());
|
||||
}
|
||||
|
||||
private static final Map<Integer, RepaymentRescheduleType> intToEnumMap = new HashMap<>();
|
||||
private static int minValue;
|
||||
|
||||
+12
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.organisation.workingdays.service;
|
||||
|
||||
import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO;
|
||||
import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
@@ -56,4 +57,15 @@ public class WorkingDaysUtil {
|
||||
public static boolean isNonWorkingDay(final WorkingDays workingDays, final LocalDate date) {
|
||||
return !isWorkingDay(workingDays, date);
|
||||
}
|
||||
|
||||
public static void updateWorkingDayIfRepaymentDateIsNonWorkingDay(final AdjustedDateDetailsDTO adjustedDateDetailsDTO, final WorkingDays workingDays) {
|
||||
final LocalDate changedScheduleDate = getOffSetDateIfNonWorkingDay(adjustedDateDetailsDTO.getChangedScheduleDate(),
|
||||
adjustedDateDetailsDTO.getNextRepaymentPeriodDueDate(), workingDays);
|
||||
adjustedDateDetailsDTO.setChangedScheduleDate(changedScheduleDate);
|
||||
}
|
||||
|
||||
public static RepaymentRescheduleType getRepaymentRescheduleType(final WorkingDays workingDays, final LocalDate date) {
|
||||
RepaymentRescheduleType rescheduleType = RepaymentRescheduleType.fromInt(workingDays.getRepaymentReschedulingType());
|
||||
return rescheduleType;
|
||||
}
|
||||
}
|
||||
|
||||
+108
@@ -700,4 +700,112 @@ public class CalendarUtils {
|
||||
}
|
||||
return monthOnDay;
|
||||
}
|
||||
|
||||
public static LocalDate getNextRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate,
|
||||
final LocalDate repaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays,
|
||||
boolean isSkipRepaymentOnFirstDayOfMonth, final Integer numberOfDays, boolean applyWorkingDays) {
|
||||
boolean isCalledFirstTime = true;
|
||||
return getNextRepaymentMeetingDate(recurringRule, seedDate, repaymentDate, loanRepaymentInterval, frequency,
|
||||
workingDays, isSkipRepaymentOnFirstDayOfMonth, numberOfDays, isCalledFirstTime, applyWorkingDays);
|
||||
}
|
||||
|
||||
public static LocalDate getNextRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate,
|
||||
final LocalDate repaymentDate, final Integer loanRepaymentInterval, final String frequency,
|
||||
boolean isSkipRepaymentOnFirstDayOfMonth, final Integer numberOfDays) {
|
||||
boolean isCalledFirstTime = true;
|
||||
final WorkingDays workingDays = null;
|
||||
boolean applyWorkingDays = false;
|
||||
return getNextRepaymentMeetingDate(recurringRule, seedDate, repaymentDate, loanRepaymentInterval, frequency,
|
||||
workingDays, isSkipRepaymentOnFirstDayOfMonth, numberOfDays, isCalledFirstTime, applyWorkingDays);
|
||||
}
|
||||
|
||||
public static LocalDate getNextRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate,
|
||||
final LocalDate repaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays,
|
||||
boolean isSkipRepaymentOnFirstDayOfMonth, final Integer numberOfDays, boolean isCalledFirstTime, boolean applyWorkingDays) {
|
||||
|
||||
final Recur recur = CalendarUtils.getICalRecur(recurringRule);
|
||||
if (recur == null) { return null; }
|
||||
LocalDate tmpDate = repaymentDate;
|
||||
|
||||
final Integer repaymentInterval = getMeetingIntervalFromFrequency(loanRepaymentInterval, frequency, recur);
|
||||
/*
|
||||
* Recurring dates should follow loanRepaymentInterval.
|
||||
*
|
||||
* e.g. The weekly meeting will have interval of 1, if the loan product
|
||||
* with fortnightly frequency will have interval of 2, to generate right
|
||||
* set of meeting dates reset interval same as loan repayment interval.
|
||||
*/
|
||||
int meetingInterval = recur.getInterval();
|
||||
if(meetingInterval < 1){
|
||||
meetingInterval = 1;
|
||||
}
|
||||
int rep = repaymentInterval<meetingInterval ? 1: repaymentInterval / meetingInterval ;
|
||||
|
||||
/*
|
||||
* Recurring dates should follow loanRepayment frequency. //e.g. daily
|
||||
* meeting frequency should support all loan products with any type of
|
||||
* frequency. to generate right set of meeting dates reset frequency
|
||||
* same as loan repayment frequency.
|
||||
*/
|
||||
if (recur.getFrequency().equals(Recur.DAILY)) {
|
||||
recur.setFrequency(frequency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Below code modified as discussed with Pramod N
|
||||
*/
|
||||
LocalDate newRepaymentDate = tmpDate;
|
||||
int newRepayment = rep;
|
||||
while (newRepayment > 0) {
|
||||
newRepaymentDate = getNextRecurringDate(recur, seedDate, newRepaymentDate);
|
||||
newRepayment--;
|
||||
}
|
||||
|
||||
LocalDate nextRepaymentDate = null;
|
||||
if (applyWorkingDays) {
|
||||
if (WorkingDaysUtil.isNonWorkingDay(workingDays, newRepaymentDate)
|
||||
&& WorkingDaysUtil.getRepaymentRescheduleType(workingDays, newRepaymentDate).isMoveToNextRepaymentDay()) {
|
||||
newRepaymentDate = getNextRepaymentMeetingDate(recurringRule, seedDate, newRepaymentDate.plusDays(1),
|
||||
loanRepaymentInterval, frequency, workingDays, isSkipRepaymentOnFirstDayOfMonth, numberOfDays, isCalledFirstTime,
|
||||
applyWorkingDays);
|
||||
} else {
|
||||
newRepaymentDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(newRepaymentDate, nextRepaymentDate, workingDays);
|
||||
}
|
||||
}
|
||||
|
||||
if(isCalledFirstTime && newRepaymentDate.equals(repaymentDate)){
|
||||
isCalledFirstTime = false;
|
||||
newRepaymentDate = getNextRepaymentMeetingDate(recurringRule, seedDate, repaymentDate.plusDays(1), loanRepaymentInterval,
|
||||
frequency, workingDays, isSkipRepaymentOnFirstDayOfMonth, numberOfDays, isCalledFirstTime, applyWorkingDays);
|
||||
}
|
||||
|
||||
if (isSkipRepaymentOnFirstDayOfMonth) {
|
||||
final LocalDate newRepaymentDateTemp = adjustRecurringDate(newRepaymentDate, numberOfDays);
|
||||
if (applyWorkingDays) {
|
||||
if (WorkingDaysUtil.isNonWorkingDay(workingDays, newRepaymentDateTemp)
|
||||
&& WorkingDaysUtil.getRepaymentRescheduleType(workingDays, newRepaymentDateTemp).isMoveToNextRepaymentDay()) {
|
||||
newRepaymentDate = getNextRepaymentMeetingDate(recurringRule, seedDate, newRepaymentDate.plusDays(1),
|
||||
loanRepaymentInterval, frequency, workingDays, isSkipRepaymentOnFirstDayOfMonth, numberOfDays,
|
||||
isCalledFirstTime, applyWorkingDays);
|
||||
} else {
|
||||
newRepaymentDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(newRepaymentDateTemp, nextRepaymentDate, workingDays);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newRepaymentDate;
|
||||
}
|
||||
|
||||
public static Integer getMeetingIntervalFromFrequency(final Integer loanRepaymentInterval, final String frequency, final Recur recur) {
|
||||
final Integer interval = 4;
|
||||
Integer repaymentInterval = loanRepaymentInterval;
|
||||
/*
|
||||
* check loanRepaymentInterval equal to 1, if repayments frequency is
|
||||
* monthly and meeting frequency is weekly, then generate repayments
|
||||
* schedule as every 4 weeks
|
||||
*/
|
||||
if (frequency.equals(Recur.MONTHLY) && recur.getFrequency().equals(Recur.WEEKLY)) {
|
||||
repaymentInterval = loanRepaymentInterval*interval;
|
||||
}
|
||||
return repaymentInterval;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-38
@@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
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.workingdays.data.AdjustedDateDetailsDTO;
|
||||
import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
@@ -121,7 +122,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
boolean isFirstRepayment = true;
|
||||
LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(
|
||||
loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
|
||||
loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, isFirstRepayment);
|
||||
final LocalDate idealDisbursementDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
|
||||
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate,
|
||||
loanApplicationTerms.getLoanCalendar(), loanApplicationTerms.getHolidayDetailDTO(), loanApplicationTerms);
|
||||
@@ -179,10 +180,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
while (!scheduleParams.getOutstandingBalance().isZero() || !scheduleParams.getDisburseDetailMap().isEmpty()) {
|
||||
LocalDate previousRepaymentDate = scheduleParams.getActualRepaymentDate();
|
||||
scheduleParams.setActualRepaymentDate(this.scheduledDateGenerator.generateNextRepaymentDate(
|
||||
scheduleParams.getActualRepaymentDate(), loanApplicationTerms, isFirstRepayment, holidayDetailDTO));
|
||||
scheduleParams.getActualRepaymentDate(), loanApplicationTerms, isFirstRepayment));
|
||||
AdjustedDateDetailsDTO adjustedDateDetailsDTO = this.scheduledDateGenerator.adjustRepaymentDate(
|
||||
scheduleParams.getActualRepaymentDate(), loanApplicationTerms, holidayDetailDTO);
|
||||
scheduleParams.setActualRepaymentDate(adjustedDateDetailsDTO.getChangedActualRepaymentDate());
|
||||
isFirstRepayment = false;
|
||||
LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(scheduleParams.getActualRepaymentDate(),
|
||||
loanApplicationTerms, holidayDetailDTO);
|
||||
LocalDate scheduledDueDate = adjustedDateDetailsDTO.getChangedScheduleDate();
|
||||
|
||||
// calculated interest start date for the period
|
||||
LocalDate periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms,
|
||||
@@ -1320,7 +1323,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
do {
|
||||
|
||||
params.setActualRepaymentDate(this.scheduledDateGenerator.generateNextRepaymentDate(params.getActualRepaymentDate(),
|
||||
loanApplicationTerms, isFirstRepayment, holidayDetailDTO));
|
||||
loanApplicationTerms, isFirstRepayment));
|
||||
if (params.getActualRepaymentDate().isAfter(currentDate)) {
|
||||
params.setActualRepaymentDate(currentDate);
|
||||
}
|
||||
@@ -2172,41 +2175,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
ArrayList<LoanTermVariationsData> dueDateVariationsDataList = new ArrayList<>();
|
||||
|
||||
|
||||
// check for date changes
|
||||
// check for date changes
|
||||
|
||||
do {
|
||||
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate,
|
||||
loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
|
||||
isFirstRepayment = false;
|
||||
lastInstallmentDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||
holidayDetailDTO);
|
||||
while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) {
|
||||
LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
|
||||
if (!variation.isSpecificToInstallment()) {
|
||||
actualRepaymentDate = variation.getDateValue();
|
||||
/*if (!isDueDateChangeApplied) {
|
||||
previousRepaymentDate = actualRepaymentDate;
|
||||
isDueDateChangeApplied = true;
|
||||
}*/
|
||||
lastInstallmentDate = actualRepaymentDate;
|
||||
}
|
||||
dueDateVariationsDataList.add(variation);
|
||||
while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) {
|
||||
LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
|
||||
if (!variation.isSpecificToInstallment()) {
|
||||
actualRepaymentDate = variation.getDateValue();
|
||||
loanApplicationTerms.setSeedDate(actualRepaymentDate);
|
||||
}
|
||||
loanTermVariationParams = applyExceptionLoanTermVariations(loanApplicationTerms, lastInstallmentDate,
|
||||
exceptionDataListIterator, instalmentNumber, totalCumulativePrincipal, totalCumulativeInterest, mc);
|
||||
} while (loanTermVariationParams != null && loanTermVariationParams.isSkipPeriod());
|
||||
|
||||
/*if (!lastInstallmentDate.isBefore(rescheduleFrom)) {
|
||||
actualRepaymentDate = previousRepaymentDate;
|
||||
if(isDueDateChangeApplied){
|
||||
int numberOfDateChangesApplied = dueDateVariationsDataList.size();
|
||||
while(numberOfDateChangesApplied >0 ){
|
||||
loanApplicationTerms.getLoanTermVariations().previousDueDateVariation();
|
||||
numberOfDateChangesApplied--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}*/
|
||||
lastInstallmentDate = variation.getDateValue();
|
||||
dueDateVariationsDataList.add(variation);
|
||||
}
|
||||
periodNumber++;
|
||||
|
||||
for (LoanTermVariationsData dueDateVariation : dueDateVariationsDataList) {
|
||||
|
||||
+113
-66
@@ -20,6 +20,7 @@ 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.data.AdjustedDateDetailsDTO;
|
||||
import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
|
||||
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
|
||||
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
|
||||
@@ -45,25 +46,26 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
LocalDate lastRepaymentDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
boolean isFirstRepayment = true;
|
||||
for (int repaymentPeriod = 1; repaymentPeriod <= numberOfRepayments; repaymentPeriod++) {
|
||||
lastRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
|
||||
lastRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment);
|
||||
isFirstRepayment = false;
|
||||
}
|
||||
lastRepaymentDate = adjustRepaymentDate(lastRepaymentDate, loanApplicationTerms, holidayDetailDTO);
|
||||
lastRepaymentDate = adjustRepaymentDate(lastRepaymentDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
|
||||
return lastRepaymentDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate generateNextRepaymentDate(final LocalDate lastRepaymentDate, final LoanApplicationTerms loanApplicationTerms,
|
||||
boolean isFirstRepayment, final HolidayDetailDTO holidayDetailDTO) {
|
||||
boolean isFirstRepayment) {
|
||||
final LocalDate firstRepaymentPeriodDate = loanApplicationTerms.getCalculatedRepaymentsStartingFromLocalDate();
|
||||
LocalDate dueRepaymentPeriodDate = null;
|
||||
Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
|
||||
if (isFirstRepayment && firstRepaymentPeriodDate != null) {
|
||||
dueRepaymentPeriodDate = firstRepaymentPeriodDate;
|
||||
} else {
|
||||
LocalDate seedDate = null;
|
||||
String reccuringString = null;
|
||||
Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
|
||||
dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate, null,
|
||||
null);
|
||||
loanApplicationTerms.getRepaymentEvery(), lastRepaymentDate);
|
||||
dueRepaymentPeriodDate = CalendarUtils.adjustDate(dueRepaymentPeriodDate, loanApplicationTerms.getSeedDate(),
|
||||
loanApplicationTerms.getRepaymentPeriodFrequencyType());
|
||||
if (currentCalendar != null) {
|
||||
@@ -79,8 +81,6 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
}
|
||||
|
||||
// get the start date from the calendar history
|
||||
LocalDate seedDate = null;
|
||||
String reccuringString = null;
|
||||
if (calendarHistory == null) {
|
||||
seedDate = currentCalendar.getStartDateLocalDate();
|
||||
reccuringString = currentCalendar.getRecurrence();
|
||||
@@ -89,80 +89,130 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
reccuringString = calendarHistory.getRecurrence();
|
||||
}
|
||||
|
||||
dueRepaymentPeriodDate = CalendarUtils.getNewRepaymentMeetingDate(reccuringString, seedDate, lastRepaymentDate.plusDays(1),
|
||||
dueRepaymentPeriodDate = CalendarUtils.getNextRepaymentMeetingDate(reccuringString, seedDate, lastRepaymentDate,
|
||||
loanApplicationTerms.getRepaymentEvery(),
|
||||
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(loanApplicationTerms.getLoanTermPeriodFrequencyType()),
|
||||
holidayDetailDTO.getWorkingDays(), loanApplicationTerms.isSkipRepaymentOnFirstDayofMonth(),
|
||||
loanApplicationTerms.isSkipRepaymentOnFirstDayofMonth(),
|
||||
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;
|
||||
}
|
||||
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
|
||||
public LocalDate adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate, final LoanApplicationTerms loanApplicationTerms,
|
||||
final HolidayDetailDTO holidayDetailDTO) {
|
||||
public AdjustedDateDetailsDTO adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate,
|
||||
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
|
||||
final LocalDate adjustedDate = dueRepaymentPeriodDate;
|
||||
return getAdjustedDateDetailsDTO(dueRepaymentPeriodDate, loanApplicationTerms, holidayDetailDTO, adjustedDate);
|
||||
}
|
||||
|
||||
LocalDate adjustedDate = dueRepaymentPeriodDate;
|
||||
private AdjustedDateDetailsDTO getAdjustedDateDetailsDTO(final LocalDate dueRepaymentPeriodDate,
|
||||
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO, final LocalDate adjustedDate) {
|
||||
final boolean isFirstRepayment = false;
|
||||
final LocalDate nextRepaymentPeriodDueDate = generateNextRepaymentDate(adjustedDate, loanApplicationTerms, isFirstRepayment);
|
||||
final AdjustedDateDetailsDTO newAdjustedDateDetailsDTO = new AdjustedDateDetailsDTO(adjustedDate, dueRepaymentPeriodDate,
|
||||
nextRepaymentPeriodDueDate);
|
||||
return recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(newAdjustedDateDetailsDTO,
|
||||
loanApplicationTerms, holidayDetailDTO, isFirstRepayment);
|
||||
}
|
||||
|
||||
LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), adjustedDate, loanApplicationTerms.getNthDay(),
|
||||
loanApplicationTerms.getWeekDayType());
|
||||
|
||||
final RepaymentRescheduleType rescheduleType = RepaymentRescheduleType.fromInt(holidayDetailDTO.getWorkingDays()
|
||||
.getRepaymentReschedulingType());
|
||||
/**
|
||||
* Recursively checking non working days and holidays and working days
|
||||
* exemption to generate next repayment period date Base on the
|
||||
* configuration
|
||||
*
|
||||
* @param adjustedDateDetailsDTO
|
||||
* @param loanApplicationTerms
|
||||
* @param holidayDetailDTO
|
||||
* @param nextRepaymentPeriodDueDate
|
||||
* @param rescheduleType
|
||||
* @param isFirstRepayment
|
||||
* @return
|
||||
*/
|
||||
private AdjustedDateDetailsDTO recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(
|
||||
final AdjustedDateDetailsDTO adjustedDateDetailsDTO, final LoanApplicationTerms loanApplicationTerms,
|
||||
final HolidayDetailDTO holidayDetailDTO, final boolean isFirstRepayment) {
|
||||
|
||||
checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
|
||||
checkAndUpdateWorkingDayIfRepaymentDateIsHolidayDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
|
||||
/**
|
||||
* Fix for https://mifosforge.jira.com/browse/MIFOSX-1357
|
||||
* Check Changed Schedule Date is holiday or is not a working day Then
|
||||
* re-call this method to get the non holiday and working day
|
||||
*/
|
||||
// recursively check for the next working meeting day.
|
||||
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), nextDueRepaymentPeriodDate)
|
||||
&& rescheduleType == RepaymentRescheduleType.MOVE_TO_NEXT_REPAYMENT_MEETING_DAY) {
|
||||
|
||||
nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), nextDueRepaymentPeriodDate, loanApplicationTerms.getNthDay(),
|
||||
loanApplicationTerms.getWeekDayType());
|
||||
nextDueRepaymentPeriodDate = CalendarUtils.adjustDate(nextDueRepaymentPeriodDate, loanApplicationTerms.getSeedDate(),
|
||||
loanApplicationTerms.getRepaymentPeriodFrequencyType());
|
||||
|
||||
if ((holidayDetailDTO.isHolidayEnabled() && HolidayUtil.getApplicableHoliday(adjustedDateDetailsDTO.getChangedScheduleDate(),
|
||||
holidayDetailDTO.getHolidays()) != null)
|
||||
|| WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate())) {
|
||||
recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(adjustedDateDetailsDTO,
|
||||
loanApplicationTerms, holidayDetailDTO, isFirstRepayment);
|
||||
}
|
||||
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate,
|
||||
holidayDetailDTO.getWorkingDays());
|
||||
return adjustedDateDetailsDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method to check and update the working day if repayment date is
|
||||
* holiday
|
||||
*
|
||||
* @param adjustedDateDetailsDTO
|
||||
* @param holidayDetailDTO
|
||||
* @param loanApplicationTerms
|
||||
* @param isFirstRepayment
|
||||
*/
|
||||
private void checkAndUpdateWorkingDayIfRepaymentDateIsHolidayDay(final AdjustedDateDetailsDTO adjustedDateDetailsDTO,
|
||||
final HolidayDetailDTO holidayDetailDTO, final LoanApplicationTerms loanApplicationTerms, final boolean isFirstRepayment) {
|
||||
if (holidayDetailDTO.isHolidayEnabled()) {
|
||||
adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidayDetailDTO.getHolidays());
|
||||
Holiday applicableHolidayForNewAdjustedDate = null;
|
||||
while ((applicableHolidayForNewAdjustedDate = HolidayUtil.getApplicableHoliday(adjustedDateDetailsDTO.getChangedScheduleDate(),
|
||||
holidayDetailDTO.getHolidays())) != null) {
|
||||
if (applicableHolidayForNewAdjustedDate.getReScheduleType().isResheduleToNextRepaymentDate()) {
|
||||
LocalDate nextRepaymentPeriodDueDate = adjustedDateDetailsDTO.getChangedActualRepaymentDate();
|
||||
while (!nextRepaymentPeriodDueDate.isAfter(adjustedDateDetailsDTO.getChangedScheduleDate())) {
|
||||
nextRepaymentPeriodDueDate = generateNextRepaymentDate(nextRepaymentPeriodDueDate, loanApplicationTerms,
|
||||
isFirstRepayment);
|
||||
}
|
||||
adjustedDateDetailsDTO.setChangedScheduleDate(nextRepaymentPeriodDueDate);
|
||||
adjustedDateDetailsDTO.setNextRepaymentPeriodDueDate(nextRepaymentPeriodDueDate);
|
||||
adjustedDateDetailsDTO.setChangedActualRepaymentDate(adjustedDateDetailsDTO.getChangedScheduleDate());
|
||||
} else {
|
||||
HolidayUtil.updateRepaymentRescheduleDateToWorkingDayIfItIsHoliday(adjustedDateDetailsDTO,
|
||||
applicableHolidayForNewAdjustedDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adjustedDate;
|
||||
/**
|
||||
* This method to check and update the working day if repayment date is non
|
||||
* working day
|
||||
*
|
||||
* @param adjustedDateDetailsDTO
|
||||
* @param holidayDetailDTO
|
||||
* @param isFirstRepayment
|
||||
* @param loanApplicationTerms
|
||||
*/
|
||||
private void checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(final AdjustedDateDetailsDTO adjustedDateDetailsDTO,
|
||||
final HolidayDetailDTO holidayDetailDTO, final LoanApplicationTerms loanApplicationTerms, final boolean isFirstRepayment) {
|
||||
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate())) {
|
||||
if (WorkingDaysUtil.getRepaymentRescheduleType(holidayDetailDTO.getWorkingDays(),
|
||||
adjustedDateDetailsDTO.getChangedScheduleDate()).isMoveToNextRepaymentDay()) {
|
||||
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(),
|
||||
adjustedDateDetailsDTO.getNextRepaymentPeriodDueDate())
|
||||
|| adjustedDateDetailsDTO.getChangedScheduleDate().isAfter(adjustedDateDetailsDTO.getNextRepaymentPeriodDueDate())) {
|
||||
final LocalDate nextRepaymentPeriodDueDate = generateNextRepaymentDate(
|
||||
adjustedDateDetailsDTO.getNextRepaymentPeriodDueDate(), loanApplicationTerms, isFirstRepayment);
|
||||
adjustedDateDetailsDTO.setNextRepaymentPeriodDueDate(nextRepaymentPeriodDueDate);
|
||||
}
|
||||
}
|
||||
WorkingDaysUtil.updateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO.getWorkingDays());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate getRepaymentPeriodDate(final PeriodFrequencyType frequency, final int repaidEvery, final LocalDate startDate,
|
||||
Integer nthDay, DayOfWeekType dayOfWeek) {
|
||||
public LocalDate getRepaymentPeriodDate(final PeriodFrequencyType frequency, final int repaidEvery, final LocalDate startDate) {
|
||||
LocalDate dueRepaymentPeriodDate = startDate;
|
||||
switch (frequency) {
|
||||
case DAYS:
|
||||
@@ -173,9 +223,6 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
break;
|
||||
case MONTHS:
|
||||
dueRepaymentPeriodDate = startDate.plusMonths(repaidEvery);
|
||||
if (!(nthDay == null || dayOfWeek == null || dayOfWeek == DayOfWeekType.INVALID)) {
|
||||
dueRepaymentPeriodDate = adjustToNthWeekDay(dueRepaymentPeriodDate, nthDay, dayOfWeek.getValue());
|
||||
}
|
||||
break;
|
||||
case YEARS:
|
||||
dueRepaymentPeriodDate = startDate.plusYears(repaidEvery);
|
||||
@@ -282,10 +329,10 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
LocalDate generatedDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
boolean isFirstRepayment = true;
|
||||
while (!generatedDate.isAfter(lastRepaymentDate)) {
|
||||
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
|
||||
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
|
||||
isFirstRepayment = false;
|
||||
}
|
||||
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO);
|
||||
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
|
||||
return generatedDate;
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -183,7 +183,7 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private Money adjustPrincipalForFlatLoans;
|
||||
|
||||
private final LocalDate seedDate;
|
||||
private LocalDate seedDate;
|
||||
|
||||
private final CalendarHistoryDataWrapper calendarHistoryDataWrapper;
|
||||
|
||||
@@ -1742,5 +1742,9 @@ public final class LoanApplicationTerms {
|
||||
public void updateTotalInterestAccounted(Money totalInterestAccounted){
|
||||
this.totalInterestAccounted = totalInterestAccounted;
|
||||
}
|
||||
|
||||
public void setSeedDate(LocalDate seedDate) {
|
||||
this.seedDate = seedDate;
|
||||
}
|
||||
|
||||
}
|
||||
+5
-6
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO;
|
||||
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||
import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
@@ -32,14 +33,12 @@ public interface ScheduledDateGenerator {
|
||||
final LocalDate firstRepaymentDate, final Calendar loanCalendar, final HolidayDetailDTO holidayDetailDTO,
|
||||
final LoanApplicationTerms loanApplicationTerms);
|
||||
|
||||
LocalDate generateNextRepaymentDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms, boolean isFirstRepayment,
|
||||
LocalDate generateNextRepaymentDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms, boolean isFirstRepayment);
|
||||
|
||||
AdjustedDateDetailsDTO adjustRepaymentDate(LocalDate dueRepaymentPeriodDate, LoanApplicationTerms loanApplicationTerms,
|
||||
final HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
LocalDate adjustRepaymentDate(LocalDate dueRepaymentPeriodDate, LoanApplicationTerms loanApplicationTerms,
|
||||
final HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
LocalDate getRepaymentPeriodDate(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate, Integer nthDay,
|
||||
DayOfWeekType dayOfWeek);
|
||||
LocalDate getRepaymentPeriodDate(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate);
|
||||
|
||||
Boolean isDateFallsInSchedule(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate, LocalDate date);
|
||||
|
||||
|
||||
+2
-2
@@ -114,10 +114,10 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
|
||||
if (rescheduleFromDate.isBefore(loanTermVariation.getTermApplicableFrom())) {
|
||||
LocalDate applicableDate = this.scheduledDateGenerator.generateNextRepaymentDate(rescheduleFromDate, loanApplicationTerms,
|
||||
false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
false);
|
||||
if (loanTermVariation.getTermApplicableFrom().equals(applicableDate)) {
|
||||
LocalDate adjustedDate = this.scheduledDateGenerator.generateNextRepaymentDate(adjustedApplicableDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
loanApplicationTerms, false);
|
||||
loanTermVariation.setApplicableFromDate(adjustedDate);
|
||||
loanTermVariationsData.add(loanTermVariation);
|
||||
}
|
||||
|
||||
+2
-2
@@ -410,9 +410,9 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
} else if (!activeLoanTermVariation.fetchTermApplicaDate().isBefore(fromScheduleDate)) {
|
||||
while (currentScheduleDate.isBefore(activeLoanTermVariation.fetchTermApplicaDate())) {
|
||||
currentScheduleDate = this.scheduledDateGenerator.generateNextRepaymentDate(currentScheduleDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
loanApplicationTerms, false);
|
||||
modifiedScheduleDate = this.scheduledDateGenerator.generateNextRepaymentDate(modifiedScheduleDate,
|
||||
loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
|
||||
loanApplicationTerms, false);
|
||||
changeMap.put(currentScheduleDate, modifiedScheduleDate);
|
||||
}
|
||||
if (changeMap.containsKey(activeLoanTermVariation.fetchTermApplicaDate())) {
|
||||
|
||||
+1
-1
@@ -2421,7 +2421,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
while (!startDate.isAfter(DateUtils.getLocalDateOfTenant())) {
|
||||
scheduleDates.put(frequencyNunber++, startDate.minusDays(diff.intValue()));
|
||||
LocalDate scheduleDate = scheduledDateGenerator.getRepaymentPeriodDate(PeriodFrequencyType.fromInt(feeFrequency),
|
||||
chargeDefinition.feeInterval(), startDate, null, null);
|
||||
chargeDefinition.feeInterval(), startDate);
|
||||
|
||||
startDate = scheduleDate;
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
--
|
||||
-- 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_holiday
|
||||
MODIFY COLUMN repayments_rescheduled_to DATETIME NULL DEFAULT NULL,
|
||||
ADD COLUMN `rescheduling_type` INT(5) NOT NULL DEFAULT '2';
|
||||
Reference in New Issue
Block a user