AL-2-reschedule-loan-first-pay-day-holiday-fix

This commit is contained in:
admin
2020-07-30 18:40:46 +05:30
parent 68e31975d4
commit ed718909bd
10 changed files with 97 additions and 14 deletions
@@ -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;
}
@@ -94,4 +94,6 @@ public interface ConfigurationDomainService {
Integer retrieveOTPLiveTime();
boolean isSubRatesEnabled();
boolean isFirstRepaymentDateAfterRescheduleAllowedOnHoliday();
}
@@ -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";
@@ -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;
}
}
@@ -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);
}
/**
@@ -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) {
@@ -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;
@@ -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;
}
}
@@ -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;
}
@@ -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');