Merge pull request #1209 from fynmanoj/AL-2
FINERACT-1106 reschedule-loan-first-pay-day-holiday-fix
This commit is contained in:
+11
-3
@@ -88,9 +88,9 @@ public class GlobalConfigurationHelper {
|
||||
ArrayList<HashMap> expectedGlobalConfigurations = getAllDefaultGlobalConfigurations();
|
||||
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
|
||||
|
||||
// There are currently 27 global configurations.
|
||||
Assertions.assertEquals(28, expectedGlobalConfigurations.size());
|
||||
Assertions.assertEquals(28, actualGlobalConfigurations.size());
|
||||
// There are currently 29 global configurations.
|
||||
Assertions.assertEquals(29, expectedGlobalConfigurations.size());
|
||||
Assertions.assertEquals(29, actualGlobalConfigurations.size());
|
||||
|
||||
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
|
||||
|
||||
@@ -346,6 +346,14 @@ public class GlobalConfigurationHelper {
|
||||
enableSubRatesDefault.put("trapDoor", false);
|
||||
defaults.add(enableSubRatesDefault);
|
||||
|
||||
HashMap<String, Object> isFirstPaydayAllowedOnHoliday = new HashMap<>();
|
||||
isFirstPaydayAllowedOnHoliday.put("id", 33);
|
||||
isFirstPaydayAllowedOnHoliday.put("name", "loan-reschedule-is-first-payday-allowed-on-holiday");
|
||||
isFirstPaydayAllowedOnHoliday.put("value", 0);
|
||||
isFirstPaydayAllowedOnHoliday.put("enabled", false);
|
||||
isFirstPaydayAllowedOnHoliday.put("trapDoor", false);
|
||||
defaults.add(isFirstPaydayAllowedOnHoliday);
|
||||
|
||||
return defaults;
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -94,4 +94,6 @@ public interface ConfigurationDomainService {
|
||||
Integer retrieveOTPLiveTime();
|
||||
|
||||
boolean isSubRatesEnabled();
|
||||
|
||||
boolean isFirstRepaymentDateAfterRescheduleAllowedOnHoliday();
|
||||
}
|
||||
|
||||
+5
@@ -252,6 +252,11 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
return getGlobalConfigurationPropertyData("skip-repayment-on-first-day-of-month").isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFirstRepaymentDateAfterRescheduleAllowedOnHoliday() {
|
||||
return getGlobalConfigurationPropertyData("loan-reschedule-is-first-payday-allowed-on-holiday").isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long retreivePeroidInNumberOfDaysForSkipMeetingDate() {
|
||||
final String propertyName = "skip-repayment-on-first-day-of-month";
|
||||
|
||||
+8
-1
@@ -43,6 +43,7 @@ public class ScheduleGeneratorDTO {
|
||||
final Integer numberOfdays;
|
||||
final boolean isSkipRepaymentOnFirstDayofMonth;
|
||||
final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled;
|
||||
final boolean isFirstRepaymentDateAllowedOnHoliday;
|
||||
|
||||
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO,
|
||||
@@ -50,7 +51,8 @@ public class ScheduleGeneratorDTO {
|
||||
final LocalDate recalculateFrom, final Long overdurPenaltyWaitPeriod, final FloatingRateDTO floatingRateDTO,
|
||||
final Calendar calendar, final CalendarHistoryDataWrapper calendarHistoryDataWrapper,
|
||||
final Boolean isInterestChargedFromDateAsDisbursementDateEnabled, final Integer numberOfdays,
|
||||
final boolean isSkipRepaymentOnFirstDayofMonth, final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled) {
|
||||
final boolean isSkipRepaymentOnFirstDayofMonth, final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled,
|
||||
final boolean isFirstRepaymentDateAllowedOnHoliday) {
|
||||
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.applicationCurrency = applicationCurrency;
|
||||
@@ -67,6 +69,7 @@ public class ScheduleGeneratorDTO {
|
||||
this.numberOfdays = numberOfdays;
|
||||
this.isSkipRepaymentOnFirstDayofMonth = isSkipRepaymentOnFirstDayofMonth;
|
||||
this.isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled;
|
||||
this.isFirstRepaymentDateAllowedOnHoliday = isFirstRepaymentDateAllowedOnHoliday;
|
||||
}
|
||||
|
||||
public LoanScheduleGeneratorFactory getLoanScheduleFactory() {
|
||||
@@ -141,4 +144,8 @@ public class ScheduleGeneratorDTO {
|
||||
return this.isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled;
|
||||
}
|
||||
|
||||
public boolean isFirstRepaymentDateAllowedOnHoliday() {
|
||||
return isFirstRepaymentDateAllowedOnHoliday;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -5486,7 +5486,7 @@ public class Loan extends AbstractPersistableCustom {
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
|
||||
holidayDetailDTO, allowCompoundingOnEod);
|
||||
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday());
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
@@ -5764,7 +5764,7 @@ public class Loan extends AbstractPersistableCustom {
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod);
|
||||
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+26
@@ -29,12 +29,15 @@ import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
|
||||
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.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
@@ -2341,6 +2344,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
periods.clear();
|
||||
}
|
||||
LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.charges(), holidayDetailDTO, loanScheduleParams);
|
||||
// check if first installment after reschedule is a holiday
|
||||
if (!loanApplicationTerms.isFirstRepaymentDateAllowedOnHoliday()) {
|
||||
checkIfFirstRepaymentDateisOnHolidayOrNonWorkingDay(holidayDetailDTO, loanScheduleModel);
|
||||
}
|
||||
|
||||
for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) {
|
||||
if (loanScheduleModelPeriod.isRepaymentPeriod()) {
|
||||
// adding newly created repayment periods to installments
|
||||
@@ -2352,6 +2360,24 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges);
|
||||
}
|
||||
|
||||
private void checkIfFirstRepaymentDateisOnHolidayOrNonWorkingDay(HolidayDetailDTO holidayDetailDTO,
|
||||
LoanScheduleModel loanScheduleModel) {
|
||||
|
||||
if (loanScheduleModel != null && loanScheduleModel.getPeriods() != null && loanScheduleModel.getPeriods().iterator() != null
|
||||
&& loanScheduleModel.getPeriods().iterator().next() != null
|
||||
&& loanScheduleModel.getPeriods().iterator().next().periodDueDate() != null) {
|
||||
|
||||
if (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(),
|
||||
loanScheduleModel.getPeriods().iterator().next().periodDueDate())
|
||||
|| HolidayUtil.getApplicableHoliday(loanScheduleModel.getPeriods().iterator().next().periodDueDate(),
|
||||
holidayDetailDTO.getHolidays()) != null) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.first.installment.date.after.reschedule.should.be.a.working.day",
|
||||
"As per configiration, the first repayment date after reschedule should be a working day");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom,
|
||||
MonetaryCurrency currency) {
|
||||
|
||||
+1
@@ -62,6 +62,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
LocalDate dueRepaymentPeriodDate = null;
|
||||
if (isFirstRepayment && firstRepaymentPeriodDate != null) {
|
||||
dueRepaymentPeriodDate = firstRepaymentPeriodDate;
|
||||
|
||||
} else {
|
||||
LocalDate seedDate = null;
|
||||
String reccuringString = null;
|
||||
|
||||
+16
-7
@@ -190,6 +190,8 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private final boolean isSkipRepaymentOnFirstDayOfMonth;
|
||||
|
||||
private final boolean isFirstRepaymentDateAllowedOnHoliday;
|
||||
|
||||
private final HolidayDetailDTO holidayDetailDTO;
|
||||
|
||||
private final Set<Integer> periodNumbersApplicableForPrincipalGrace = new HashSet<>();
|
||||
@@ -243,7 +245,7 @@ public final class LoanApplicationTerms {
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
||||
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization);
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false);
|
||||
|
||||
}
|
||||
|
||||
@@ -269,7 +271,7 @@ public final class LoanApplicationTerms {
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType, restCalendarInstance,
|
||||
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
|
||||
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod);
|
||||
calendarHistoryDataWrapper, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, false);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -286,7 +288,8 @@ public final class LoanApplicationTerms {
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount,
|
||||
BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations,
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, final Integer numberOfDays,
|
||||
final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod) {
|
||||
final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
||||
final boolean isFirstRepaymentDateAllowedOnHoliday) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -324,7 +327,7 @@ public final class LoanApplicationTerms {
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
||||
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO,
|
||||
allowCompoundingOnEod, isEqualAmortization);
|
||||
allowCompoundingOnEod, isEqualAmortization, isFirstRepaymentDateAllowedOnHoliday);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -383,7 +386,7 @@ public final class LoanApplicationTerms {
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
||||
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization);
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false);
|
||||
|
||||
}
|
||||
|
||||
@@ -409,7 +412,8 @@ public final class LoanApplicationTerms {
|
||||
applicationTerms.loanCalendar, applicationTerms.approvedPrincipal.getAmount(), loanTermVariations,
|
||||
applicationTerms.calendarHistoryDataWrapper, applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||
applicationTerms.numberOfDays, applicationTerms.isSkipRepaymentOnFirstDayOfMonth, applicationTerms.holidayDetailDTO,
|
||||
applicationTerms.allowCompoundingOnEod, applicationTerms.isEqualAmortization);
|
||||
applicationTerms.allowCompoundingOnEod, applicationTerms.isEqualAmortization,
|
||||
applicationTerms.isFirstRepaymentDateAllowedOnHoliday);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -433,7 +437,7 @@ public final class LoanApplicationTerms {
|
||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||
final Integer numberOfDays, final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO,
|
||||
final boolean allowCompoundingOnEod, final boolean isEqualAmortization) {
|
||||
final boolean allowCompoundingOnEod, final boolean isEqualAmortization, final boolean isFirstRepaymentDateAllowedOnHoliday) {
|
||||
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
@@ -506,6 +510,7 @@ public final class LoanApplicationTerms {
|
||||
this.totalInterestAccounted = principal.zero();
|
||||
this.totalPrincipalAccounted = principal.zero();
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
this.isFirstRepaymentDateAllowedOnHoliday = isFirstRepaymentDateAllowedOnHoliday;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -1769,4 +1774,8 @@ public final class LoanApplicationTerms {
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
}
|
||||
|
||||
public boolean isFirstRepaymentDateAllowedOnHoliday() {
|
||||
return isFirstRepaymentDateAllowedOnHoliday;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-1
@@ -140,11 +140,14 @@ public class LoanUtilService {
|
||||
final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = this.configurationDomainService
|
||||
.isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
|
||||
|
||||
boolean isFirstRepaymentDateAllowedOnHoliday = this.configurationDomainService
|
||||
.isFirstRepaymentDateAfterRescheduleAllowedOnHoliday();
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO, calendar, calendarHistoryDataWrapper,
|
||||
isInterestChargedFromDateAsDisbursementDateEnabled, numberOfDays, isSkipRepaymentOnFirstMonth,
|
||||
isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled);
|
||||
isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled, isFirstRepaymentDateAllowedOnHoliday);
|
||||
|
||||
return scheduleGeneratorDTO;
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
|
||||
INSERT INTO `c_configuration` (`name`, `value`, `date_value`, `enabled`, `is_trap_door`, `description`)
|
||||
VALUES
|
||||
('loan-reschedule-is-first-payday-allowed-on-holiday', 0, NULL, 0, 0, 'If enabled, while loan reschedule the first repayment date can be on a holiday/non working day');
|
||||
Reference in New Issue
Block a user