FINERACT-1443-loan-interest-recover-policy-fix-when-interest-more-than-EMI (#2033)
This commit is contained in:
+4
-2
@@ -75,6 +75,10 @@ public interface ConfigurationDomainService {
|
|||||||
|
|
||||||
boolean isSkippingMeetingOnFirstDayOfMonthEnabled();
|
boolean isSkippingMeetingOnFirstDayOfMonthEnabled();
|
||||||
|
|
||||||
|
boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI();
|
||||||
|
|
||||||
|
boolean isPrincipalCompoundingDisabledForOverdueLoans();
|
||||||
|
|
||||||
Long retreivePeroidInNumberOfDaysForSkipMeetingDate();
|
Long retreivePeroidInNumberOfDaysForSkipMeetingDate();
|
||||||
|
|
||||||
boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
|
boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
|
||||||
@@ -97,8 +101,6 @@ public interface ConfigurationDomainService {
|
|||||||
|
|
||||||
boolean isFirstRepaymentDateAfterRescheduleAllowedOnHoliday();
|
boolean isFirstRepaymentDateAfterRescheduleAllowedOnHoliday();
|
||||||
|
|
||||||
boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI();
|
|
||||||
|
|
||||||
String getAccountMappingForPaymentType();
|
String getAccountMappingForPaymentType();
|
||||||
|
|
||||||
String getAccountMappingForCharge();
|
String getAccountMappingForCharge();
|
||||||
|
|||||||
+7
-2
@@ -259,8 +259,13 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI() {
|
public boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI() {
|
||||||
return getGlobalConfigurationPropertyData("is-interest-to-be-appropriated-equally-when-greater-than-emi").isEnabled();
|
return getGlobalConfigurationPropertyData("is-interest-to-be-recovered-first-when-greater-than-emi").isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPrincipalCompoundingDisabledForOverdueLoans() {
|
||||||
|
return getGlobalConfigurationPropertyData("is-principal-compounding-disabled-for-overdue-loans").isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+12
-5
@@ -44,7 +44,8 @@ public class ScheduleGeneratorDTO {
|
|||||||
final boolean isSkipRepaymentOnFirstDayofMonth;
|
final boolean isSkipRepaymentOnFirstDayofMonth;
|
||||||
final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled;
|
final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled;
|
||||||
final boolean isFirstRepaymentDateAllowedOnHoliday;
|
final boolean isFirstRepaymentDateAllowedOnHoliday;
|
||||||
final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI;
|
final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI;
|
||||||
|
final boolean isPrincipalCompoundingDisabledForOverdueLoans;
|
||||||
|
|
||||||
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO,
|
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO,
|
||||||
@@ -53,7 +54,8 @@ public class ScheduleGeneratorDTO {
|
|||||||
final Calendar calendar, final CalendarHistoryDataWrapper calendarHistoryDataWrapper,
|
final Calendar calendar, final CalendarHistoryDataWrapper calendarHistoryDataWrapper,
|
||||||
final Boolean isInterestChargedFromDateAsDisbursementDateEnabled, final Integer numberOfdays,
|
final Boolean isInterestChargedFromDateAsDisbursementDateEnabled, final Integer numberOfdays,
|
||||||
final boolean isSkipRepaymentOnFirstDayofMonth, final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled,
|
final boolean isSkipRepaymentOnFirstDayofMonth, final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled,
|
||||||
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI) {
|
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
||||||
|
final boolean isPrincipalCompoundingDisabledForOverdueLoans) {
|
||||||
|
|
||||||
this.loanScheduleFactory = loanScheduleFactory;
|
this.loanScheduleFactory = loanScheduleFactory;
|
||||||
this.applicationCurrency = applicationCurrency;
|
this.applicationCurrency = applicationCurrency;
|
||||||
@@ -71,7 +73,8 @@ public class ScheduleGeneratorDTO {
|
|||||||
this.isSkipRepaymentOnFirstDayofMonth = isSkipRepaymentOnFirstDayofMonth;
|
this.isSkipRepaymentOnFirstDayofMonth = isSkipRepaymentOnFirstDayofMonth;
|
||||||
this.isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled;
|
this.isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled;
|
||||||
this.isFirstRepaymentDateAllowedOnHoliday = isFirstRepaymentDateAllowedOnHoliday;
|
this.isFirstRepaymentDateAllowedOnHoliday = isFirstRepaymentDateAllowedOnHoliday;
|
||||||
this.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI = isInterestToBeAppropriatedEquallyWhenGreaterThanEMI;
|
this.isInterestToBeRecoveredFirstWhenGreaterThanEMI = isInterestToBeRecoveredFirstWhenGreaterThanEMI;
|
||||||
|
this.isPrincipalCompoundingDisabledForOverdueLoans = isPrincipalCompoundingDisabledForOverdueLoans;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanScheduleGeneratorFactory getLoanScheduleFactory() {
|
public LoanScheduleGeneratorFactory getLoanScheduleFactory() {
|
||||||
@@ -150,7 +153,11 @@ public class ScheduleGeneratorDTO {
|
|||||||
return isFirstRepaymentDateAllowedOnHoliday;
|
return isFirstRepaymentDateAllowedOnHoliday;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI() {
|
public boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI() {
|
||||||
return isInterestToBeAppropriatedEquallyWhenGreaterThanEMI;
|
return isInterestToBeRecoveredFirstWhenGreaterThanEMI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrincipalCompoundingDisabledForOverdueLoans() {
|
||||||
|
return isPrincipalCompoundingDisabledForOverdueLoans;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -5623,7 +5623,8 @@ public class Loan extends AbstractPersistableCustom {
|
|||||||
rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||||
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
|
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
|
||||||
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
|
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
|
||||||
scheduleGeneratorDTO.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI(), this.fixedPrincipalPercentagePerInstallment);
|
scheduleGeneratorDTO.isInterestToBeRecoveredFirstWhenGreaterThanEMI(), this.fixedPrincipalPercentagePerInstallment,
|
||||||
|
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans());
|
||||||
return loanApplicationTerms;
|
return loanApplicationTerms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5905,7 +5906,7 @@ public class Loan extends AbstractPersistableCustom {
|
|||||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||||
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
|
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
|
||||||
false, this.fixedPrincipalPercentagePerInstallment);
|
false, this.fixedPrincipalPercentagePerInstallment, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+11
-59
@@ -286,8 +286,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
// will check for EMI amount greater than interest calculated
|
// will check for EMI amount greater than interest calculated
|
||||||
if (loanApplicationTerms.getFixedEmiAmount() != null
|
if (loanApplicationTerms.getFixedEmiAmount() != null
|
||||||
&& loanApplicationTerms.getFixedEmiAmount().compareTo(principalInterestForThisPeriod.interest().getAmount()) < 0
|
&& loanApplicationTerms.getFixedEmiAmount().compareTo(principalInterestForThisPeriod.interest().getAmount()) < 0) {
|
||||||
&& !loanApplicationTerms.isInterestToBeAppropriatedEquallyWhenGreaterThanEMIEnabled()) {
|
|
||||||
String errorMsg = "EMI amount must be greater than : " + principalInterestForThisPeriod.interest().getAmount();
|
String errorMsg = "EMI amount must be greater than : " + principalInterestForThisPeriod.interest().getAmount();
|
||||||
throw new MultiDisbursementEmiAmountException(errorMsg, principalInterestForThisPeriod.interest().getAmount(),
|
throw new MultiDisbursementEmiAmountException(errorMsg, principalInterestForThisPeriod.interest().getAmount(),
|
||||||
loanApplicationTerms.getFixedEmiAmount());
|
loanApplicationTerms.getFixedEmiAmount());
|
||||||
@@ -333,7 +332,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
|
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
|
||||||
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
|
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
|
||||||
totalInstallmentDue, !isCompletePeriod);
|
totalInstallmentDue, !isCompletePeriod);
|
||||||
|
if (principalInterestForThisPeriod.getRescheduleInterestPortion() != null) {
|
||||||
|
installment.setRescheduleInterestPortion(principalInterestForThisPeriod.getRescheduleInterestPortion().getAmount());
|
||||||
|
}
|
||||||
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment);
|
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment);
|
||||||
// apply loan transactions on installments to identify early/late
|
// apply loan transactions on installments to identify early/late
|
||||||
// payments for interest recalculation
|
// payments for interest recalculation
|
||||||
@@ -379,44 +380,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(Money.zero(currency));
|
scheduleParams.setTotalOutstandingInterestPaymentDueToGrace(Money.zero(currency));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loanApplicationTerms.isInterestToBeAppropriatedEquallyWhenGreaterThanEMIEnabled()
|
|
||||||
&& loanApplicationTerms.getInterestTobeApproppriated() != null
|
|
||||||
&& loanApplicationTerms.getInterestTobeApproppriated().isGreaterThanZero()) {
|
|
||||||
int emisTobeChanged = 1;
|
|
||||||
for (LoanScheduleModelPeriod installment : (List<LoanScheduleModelPeriod>) periods) {
|
|
||||||
if (!installment.isEMIFixedSpecificToInstallment()) {
|
|
||||||
emisTobeChanged++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (emisTobeChanged > 1) {
|
|
||||||
emisTobeChanged--;
|
|
||||||
}
|
|
||||||
Money interestTobeApproppriated = loanApplicationTerms.getInterestTobeApproppriated();
|
|
||||||
Money interestFraction = interestTobeApproppriated.dividedBy(emisTobeChanged, mc.getRoundingMode());
|
|
||||||
BigDecimal roundFraction = interestFraction.getAmount().remainder(BigDecimal.ONE);
|
|
||||||
interestFraction = interestFraction.minus(roundFraction);
|
|
||||||
for (LoanScheduleModelPeriod installment : (List<LoanScheduleModelPeriod>) periods) {
|
|
||||||
if (!installment.isEMIFixedSpecificToInstallment()) {
|
|
||||||
installment.addInterestAmount(interestFraction);
|
|
||||||
installment.setRescheduleInterestPortion(interestFraction.getAmount());
|
|
||||||
interestTobeApproppriated = interestTobeApproppriated.minus(interestFraction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoanScheduleModelPeriod lastInstallment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1);
|
|
||||||
|
|
||||||
if (interestTobeApproppriated.isGreaterThanZero()) {
|
|
||||||
lastInstallment.addInterestAmount(interestTobeApproppriated);
|
|
||||||
BigDecimal rescheduleInterestPortion = BigDecimal.ZERO;
|
|
||||||
if (lastInstallment.rescheduleInterestPortion() != null) {
|
|
||||||
rescheduleInterestPortion = lastInstallment.rescheduleInterestPortion();
|
|
||||||
}
|
|
||||||
lastInstallment.setRescheduleInterestPortion(rescheduleInterestPortion.add(interestTobeApproppriated.getAmount()));
|
|
||||||
}
|
|
||||||
scheduleParams.addTotalRepaymentExpected(loanApplicationTerms.getInterestTobeApproppriated());
|
|
||||||
scheduleParams.addTotalCumulativeInterest(loanApplicationTerms.getInterestTobeApproppriated());
|
|
||||||
loanApplicationTerms.setInterestTobeApproppriated(Money.zero(currency));
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine fees and penalties for charges which depends on total
|
// determine fees and penalties for charges which depends on total
|
||||||
// loan interest
|
// loan interest
|
||||||
updatePeriodsWithCharges(currency, scheduleParams, periods, nonCompoundingCharges);
|
updatePeriodsWithCharges(currency, scheduleParams, periods, nonCompoundingCharges);
|
||||||
@@ -1054,6 +1017,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
modifiedScheduledDueDate = loanTermVariationsData.getDateValue();
|
modifiedScheduledDueDate = loanTermVariationsData.getDateValue();
|
||||||
if (!loanTermVariationsData.isSpecificToInstallment()) {
|
if (!loanTermVariationsData.isSpecificToInstallment()) {
|
||||||
scheduleParams.setActualRepaymentDate(modifiedScheduledDueDate);
|
scheduleParams.setActualRepaymentDate(modifiedScheduledDueDate);
|
||||||
|
loanApplicationTerms.setNewScheduledDueDateStart(modifiedScheduledDueDate);
|
||||||
}
|
}
|
||||||
loanTermVariationsData.setProcessed(true);
|
loanTermVariationsData.setProcessed(true);
|
||||||
}
|
}
|
||||||
@@ -2397,23 +2361,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
periods.clear();
|
periods.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal rescheuleInterestPortionTobeRetained = BigDecimal.ZERO;
|
if (retainedInstallments.size() > 0
|
||||||
for (LoanRepaymentScheduleInstallment retainedInstallment : retainedInstallments) {
|
&& retainedInstallments.get(retainedInstallments.size() - 1).getRescheduleInterestPortion() != null) {
|
||||||
if (retainedInstallment.getRescheduleInterestPortion() != null) {
|
loanApplicationTerms.setInterestTobeApproppriated(
|
||||||
rescheuleInterestPortionTobeRetained = rescheuleInterestPortionTobeRetained
|
Money.of(loan.getCurrency(), retainedInstallments.get(retainedInstallments.size() - 1).getRescheduleInterestPortion()));
|
||||||
.add(retainedInstallment.getRescheduleInterestPortion());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal rescheuleInterestPortionTotal = BigDecimal.ZERO;
|
|
||||||
for (LoanRepaymentScheduleInstallment inst : loan.getRepaymentScheduleInstallments()) {
|
|
||||||
if (inst.getRescheduleInterestPortion() != null) {
|
|
||||||
rescheuleInterestPortionTotal = rescheuleInterestPortionTotal.add(inst.getRescheduleInterestPortion());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BigDecimal rescheuleInterestPortionTobeAppropriated = rescheuleInterestPortionTotal.subtract(rescheuleInterestPortionTobeRetained);
|
|
||||||
loanApplicationTerms.setInterestTobeApproppriated(Money.of(loan.getCurrency(), rescheuleInterestPortionTobeAppropriated));
|
|
||||||
LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.charges(), holidayDetailDTO, loanScheduleParams);
|
LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.charges(), holidayDetailDTO, loanScheduleParams);
|
||||||
|
|
||||||
for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) {
|
for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) {
|
||||||
@@ -2640,8 +2592,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final HolidayDetailDTO holidayDetailDTO) {
|
final HolidayDetailDTO holidayDetailDTO) {
|
||||||
LocalDate nextScheduleDate = null;
|
LocalDate nextScheduleDate = null;
|
||||||
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
||||||
nextScheduleDate = this.scheduledDateGenerator.generateNextScheduleDateStartingFromDisburseDate(startDate, loanApplicationTerms,
|
nextScheduleDate = this.scheduledDateGenerator.generateNextScheduleDateStartingFromDisburseDateOrRescheduleDate(startDate,
|
||||||
holidayDetailDTO);
|
loanApplicationTerms, holidayDetailDTO);
|
||||||
} else {
|
} else {
|
||||||
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
||||||
nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), startDate);
|
nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), startDate);
|
||||||
|
|||||||
+20
-28
@@ -113,7 +113,11 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
|||||||
compoundFee = compoundingMap.get(principal.getKey());
|
compoundFee = compoundingMap.get(principal.getKey());
|
||||||
compoundingMap.put(principal.getKey(), interestToBeCompounded.plus(compoundFee));
|
compoundingMap.put(principal.getKey(), interestToBeCompounded.plus(compoundFee));
|
||||||
}
|
}
|
||||||
balanceForInterestCalculation = balanceForInterestCalculation.plus(principal.getValue()).plus(compoundFee);
|
if (!loanApplicationTerms.isPrincipalCompoundingDisabledForOverdueLoans()) {
|
||||||
|
balanceForInterestCalculation = balanceForInterestCalculation.plus(principal.getValue());
|
||||||
|
}
|
||||||
|
balanceForInterestCalculation = balanceForInterestCalculation.plus(compoundFee);
|
||||||
|
|
||||||
if (interestRates.containsKey(principal.getKey())) {
|
if (interestRates.containsKey(principal.getKey())) {
|
||||||
loanApplicationTerms.updateAnnualNominalInterestRate(interestRates.get(principal.getKey()));
|
loanApplicationTerms.updateAnnualNominalInterestRate(interestRates.get(principal.getKey()));
|
||||||
}
|
}
|
||||||
@@ -128,42 +132,27 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
|||||||
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
interestForThisInstallment = interestForThisInstallment.plus(result.interest());
|
||||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
||||||
|
|
||||||
Money interestTobeApproppriated = loanApplicationTerms.getInterestTobeApproppriated() == null
|
if (loanApplicationTerms.isInterestToBeRecoveredFirstWhenGreaterThanEMIEnabled()
|
||||||
? Money.zero(interestForThisInstallment.getCurrency())
|
&& loanApplicationTerms.isInterestTobeApproppriated()) {
|
||||||
: loanApplicationTerms.getInterestTobeApproppriated();
|
interestForThisInstallment = interestForThisInstallment.add(loanApplicationTerms.getInterestTobeApproppriated());
|
||||||
|
loanApplicationTerms.setInterestTobeApproppriated(interestForThisInstallment.zero());
|
||||||
if (loanApplicationTerms.getFixedEmiAmount() != null
|
|
||||||
&& loanApplicationTerms.isInterestToBeAppropriatedEquallyWhenGreaterThanEMIEnabled() && interestForThisInstallment
|
|
||||||
.isGreaterThan(Money.of(interestForThisInstallment.getCurrency(), loanApplicationTerms.getFixedEmiAmount()))) {
|
|
||||||
LocalDate actualPeriodEndDate = this.scheduledDateGenerator.generateNextRepaymentDate(interestStartDate, loanApplicationTerms,
|
|
||||||
false);
|
|
||||||
PrincipalInterest tempInterest = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
|
|
||||||
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace,
|
|
||||||
balanceForInterestCalculation, interestStartDate, actualPeriodEndDate);
|
|
||||||
|
|
||||||
Money fixedEmi = Money.of(interestForThisInstallment.getCurrency(), loanApplicationTerms.getFixedEmiAmount());
|
|
||||||
|
|
||||||
if (tempInterest.interest().isGreaterThan(fixedEmi)) {
|
|
||||||
loanApplicationTerms
|
|
||||||
.setInterestTobeApproppriated(interestTobeApproppriated.plus(interestForThisInstallment.minus(fixedEmi)));
|
|
||||||
interestForThisInstallment = fixedEmi;
|
|
||||||
} else {
|
|
||||||
loanApplicationTerms.setInterestTobeApproppriated(
|
|
||||||
interestTobeApproppriated.plus(interestForThisInstallment.minus(tempInterest.interest())));
|
|
||||||
interestForThisInstallment = tempInterest.interest();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
|
|
||||||
|
|
||||||
Money interestForPeriod = interestForThisInstallment;
|
Money interestForPeriod = interestForThisInstallment;
|
||||||
if (interestForPeriod.isGreaterThanZero()) {
|
if (interestForPeriod.isGreaterThanZero()) {
|
||||||
interestForPeriod = interestForPeriod.minus(cumulatingInterestPaymentDueToGrace);
|
interestForPeriod = interestForPeriod.minus(cumulatingInterestPaymentDueToGrace);
|
||||||
} else {
|
} else {
|
||||||
interestForPeriod = cumulatingInterestDueToGrace.minus(cumulatingInterestPaymentDueToGrace);
|
interestForPeriod = cumulatingInterestDueToGrace.minus(cumulatingInterestPaymentDueToGrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance,
|
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance,
|
||||||
periodNumber, mc, interestForPeriod);
|
periodNumber, mc, interestForPeriod);
|
||||||
|
if (loanApplicationTerms.isInterestToBeRecoveredFirstWhenGreaterThanEMIEnabled() && principalForThisInstallment.isLessThanZero()
|
||||||
|
&& !loanApplicationTerms.isLastRepaymentPeriod(periodNumber)) {
|
||||||
|
loanApplicationTerms.setInterestTobeApproppriated(principalForThisInstallment.abs());
|
||||||
|
interestForThisInstallment = interestForThisInstallment.minus(loanApplicationTerms.getInterestTobeApproppriated());
|
||||||
|
principalForThisInstallment = principalForThisInstallment.zero();
|
||||||
|
}
|
||||||
|
|
||||||
// update cumulative fields for principal & interest
|
// update cumulative fields for principal & interest
|
||||||
final Money interestBroughtFowardDueToGrace = cumulatingInterestDueToGrace;
|
final Money interestBroughtFowardDueToGrace = cumulatingInterestDueToGrace;
|
||||||
@@ -173,6 +162,9 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
|||||||
principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
|
principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
|
||||||
totalCumulativePrincipalToDate, periodNumber);
|
totalCumulativePrincipalToDate, periodNumber);
|
||||||
|
|
||||||
return new PrincipalInterest(principalForThisInstallment, interestForThisInstallment, interestBroughtFowardDueToGrace);
|
PrincipalInterest principalInterest = new PrincipalInterest(principalForThisInstallment, interestForThisInstallment,
|
||||||
|
interestBroughtFowardDueToGrace);
|
||||||
|
principalInterest.setRescheduleInterestPortion(loanApplicationTerms.getInterestTobeApproppriated());
|
||||||
|
return principalInterest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
@@ -327,4 +327,22 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
|||||||
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
|
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
|
||||||
return generatedDate;
|
return generatedDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalDate generateNextScheduleDateStartingFromDisburseDateOrRescheduleDate(LocalDate lastRepaymentDate,
|
||||||
|
LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
|
|
||||||
|
LocalDate generatedDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
|
boolean isFirstRepayment = true;
|
||||||
|
if (loanApplicationTerms.getNewScheduledDueDateStart() != null) {
|
||||||
|
generatedDate = loanApplicationTerms.getNewScheduledDueDateStart();
|
||||||
|
isFirstRepayment = false;
|
||||||
|
}
|
||||||
|
while (!generatedDate.isAfter(lastRepaymentDate)) {
|
||||||
|
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
|
||||||
|
isFirstRepayment = false;
|
||||||
|
}
|
||||||
|
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
|
||||||
|
return generatedDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-18
@@ -188,7 +188,9 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
private final boolean isFirstRepaymentDateAllowedOnHoliday;
|
private final boolean isFirstRepaymentDateAllowedOnHoliday;
|
||||||
|
|
||||||
private final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI;
|
private final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI;
|
||||||
|
|
||||||
|
private boolean isPrincipalCompoundingDisabledForOverdueLoans;
|
||||||
|
|
||||||
private final HolidayDetailDTO holidayDetailDTO;
|
private final HolidayDetailDTO holidayDetailDTO;
|
||||||
|
|
||||||
@@ -209,6 +211,8 @@ public final class LoanApplicationTerms {
|
|||||||
private Money interestTobeApproppriated;
|
private Money interestTobeApproppriated;
|
||||||
private final BigDecimal fixedPrincipalPercentagePerInstallment;
|
private final BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||||
|
|
||||||
|
private LocalDate newScheduledDueDateStart;
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||||
final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType,
|
final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType,
|
||||||
@@ -230,7 +234,8 @@ public final class LoanApplicationTerms {
|
|||||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
||||||
Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, final Integer numberOfDays,
|
Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, final Integer numberOfDays,
|
||||||
boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
||||||
final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
final boolean isEqualAmortization, final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
||||||
|
final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean isPrincipalCompoundingDisabledForOverdueLoans) {
|
||||||
|
|
||||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||||
@@ -245,8 +250,9 @@ public final class LoanApplicationTerms {
|
|||||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
||||||
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
||||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false,
|
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false,
|
||||||
fixedPrincipalPercentagePerInstallment);
|
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
||||||
|
isPrincipalCompoundingDisabledForOverdueLoans);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +279,7 @@ public final class LoanApplicationTerms {
|
|||||||
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
|
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
|
||||||
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations,
|
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations,
|
||||||
calendarHistoryDataWrapper, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, false,
|
calendarHistoryDataWrapper, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, false,
|
||||||
false, null);
|
false, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||||
@@ -291,8 +297,8 @@ public final class LoanApplicationTerms {
|
|||||||
BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations,
|
BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations,
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, final Integer numberOfDays,
|
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 boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI,
|
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
||||||
final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean isPrincipalCompoundingDisabledForOverdueLoans) {
|
||||||
|
|
||||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||||
@@ -331,7 +337,8 @@ public final class LoanApplicationTerms {
|
|||||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
||||||
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO,
|
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO,
|
||||||
allowCompoundingOnEod, isEqualAmortization, isFirstRepaymentDateAllowedOnHoliday,
|
allowCompoundingOnEod, isEqualAmortization, isFirstRepaymentDateAllowedOnHoliday,
|
||||||
isInterestToBeAppropriatedEquallyWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment);
|
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
||||||
|
isPrincipalCompoundingDisabledForOverdueLoans);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||||
@@ -390,7 +397,7 @@ public final class LoanApplicationTerms {
|
|||||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||||
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
||||||
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
||||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false, null);
|
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false, null, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,8 +424,8 @@ public final class LoanApplicationTerms {
|
|||||||
applicationTerms.calendarHistoryDataWrapper, applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
applicationTerms.calendarHistoryDataWrapper, applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||||
applicationTerms.numberOfDays, applicationTerms.isSkipRepaymentOnFirstDayOfMonth, applicationTerms.holidayDetailDTO,
|
applicationTerms.numberOfDays, applicationTerms.isSkipRepaymentOnFirstDayOfMonth, applicationTerms.holidayDetailDTO,
|
||||||
applicationTerms.allowCompoundingOnEod, applicationTerms.isEqualAmortization,
|
applicationTerms.allowCompoundingOnEod, applicationTerms.isEqualAmortization,
|
||||||
applicationTerms.isFirstRepaymentDateAllowedOnHoliday, applicationTerms.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI,
|
applicationTerms.isFirstRepaymentDateAllowedOnHoliday, applicationTerms.isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
||||||
applicationTerms.fixedPrincipalPercentagePerInstallment);
|
applicationTerms.fixedPrincipalPercentagePerInstallment, applicationTerms.isPrincipalCompoundingDisabledForOverdueLoans);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
@@ -443,7 +450,8 @@ public final class LoanApplicationTerms {
|
|||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||||
final Integer numberOfDays, final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO,
|
final Integer numberOfDays, final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO,
|
||||||
final boolean allowCompoundingOnEod, final boolean isEqualAmortization, final boolean isFirstRepaymentDateAllowedOnHoliday,
|
final boolean allowCompoundingOnEod, final boolean isEqualAmortization, final boolean isFirstRepaymentDateAllowedOnHoliday,
|
||||||
final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
||||||
|
final boolean isPrincipalCompoundingDisabledForOverdueLoans) {
|
||||||
|
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.loanTermFrequency = loanTermFrequency;
|
this.loanTermFrequency = loanTermFrequency;
|
||||||
@@ -517,8 +525,9 @@ public final class LoanApplicationTerms {
|
|||||||
this.totalPrincipalAccounted = principal.zero();
|
this.totalPrincipalAccounted = principal.zero();
|
||||||
this.isEqualAmortization = isEqualAmortization;
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
this.isFirstRepaymentDateAllowedOnHoliday = isFirstRepaymentDateAllowedOnHoliday;
|
this.isFirstRepaymentDateAllowedOnHoliday = isFirstRepaymentDateAllowedOnHoliday;
|
||||||
this.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI = isInterestToBeAppropriatedEquallyWhenGreaterThanEMI;
|
this.isInterestToBeRecoveredFirstWhenGreaterThanEMI = isInterestToBeRecoveredFirstWhenGreaterThanEMI;
|
||||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||||
|
this.isPrincipalCompoundingDisabledForOverdueLoans = isPrincipalCompoundingDisabledForOverdueLoans;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||||
@@ -743,6 +752,10 @@ public final class LoanApplicationTerms {
|
|||||||
return periodNumber == numberOfRepayments;
|
return periodNumber == numberOfRepayments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLastRepaymentPeriod(final int periodNumber) {
|
||||||
|
return periodNumber == this.actualNumberOfRepayments;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* general method to calculate totalInterestDue discounting any grace settings
|
* general method to calculate totalInterestDue discounting any grace settings
|
||||||
*/
|
*/
|
||||||
@@ -1248,7 +1261,7 @@ public final class LoanApplicationTerms {
|
|||||||
Money interestDue = Money.zero(outstandingBalance.getCurrency());
|
Money interestDue = Money.zero(outstandingBalance.getCurrency());
|
||||||
|
|
||||||
final BigDecimal periodicInterestRate = periodicInterestRate(calculator, mc, this.daysInMonthType, this.daysInYearType,
|
final BigDecimal periodicInterestRate = periodicInterestRate(calculator, mc, this.daysInMonthType, this.daysInYearType,
|
||||||
periodStartDate, periodEndDate);
|
periodStartDate, periodEndDate);// 0.021232877 ob:14911.64
|
||||||
interestDue = outstandingBalance.multiplyRetainScale(periodicInterestRate, mc.getRoundingMode());
|
interestDue = outstandingBalance.multiplyRetainScale(periodicInterestRate, mc.getRoundingMode());
|
||||||
|
|
||||||
return interestDue;
|
return interestDue;
|
||||||
@@ -1334,7 +1347,6 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
private Money calculatePrincipalDueForInstallment(final int periodNumber, final Money totalDuePerInstallment,
|
private Money calculatePrincipalDueForInstallment(final int periodNumber, final Money totalDuePerInstallment,
|
||||||
final Money periodInterest) {
|
final Money periodInterest) {
|
||||||
|
|
||||||
Money principal = totalDuePerInstallment.minus(periodInterest);
|
Money principal = totalDuePerInstallment.minus(periodInterest);
|
||||||
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
||||||
principal = principal.zero();
|
principal = principal.zero();
|
||||||
@@ -1795,15 +1807,30 @@ public final class LoanApplicationTerms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Money getInterestTobeApproppriated() {
|
public Money getInterestTobeApproppriated() {
|
||||||
return interestTobeApproppriated;
|
return interestTobeApproppriated == null ? this.principal.zero() : interestTobeApproppriated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInterestTobeApproppriated(Money interestTobeApproppriated) {
|
public void setInterestTobeApproppriated(Money interestTobeApproppriated) {
|
||||||
this.interestTobeApproppriated = interestTobeApproppriated;
|
this.interestTobeApproppriated = interestTobeApproppriated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMIEnabled() {
|
public Boolean isInterestTobeApproppriated() {
|
||||||
return isInterestToBeAppropriatedEquallyWhenGreaterThanEMI;
|
return interestTobeApproppriated != null && interestTobeApproppriated.isGreaterThanZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInterestToBeRecoveredFirstWhenGreaterThanEMIEnabled() {
|
||||||
|
return isInterestToBeRecoveredFirstWhenGreaterThanEMI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrincipalCompoundingDisabledForOverdueLoans() {
|
||||||
|
return isPrincipalCompoundingDisabledForOverdueLoans;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getNewScheduledDueDateStart() {
|
||||||
|
return newScheduledDueDateStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNewScheduledDueDateStart(LocalDate newScheduledDueDateStart) {
|
||||||
|
this.newScheduledDueDateStart = newScheduledDueDateStart;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -25,6 +25,7 @@ public class PrincipalInterest {
|
|||||||
private final Money principal;
|
private final Money principal;
|
||||||
private final Money interest;
|
private final Money interest;
|
||||||
private final Money interestPaymentDueToGrace;
|
private final Money interestPaymentDueToGrace;
|
||||||
|
private Money rescheduleInterestPortion;
|
||||||
|
|
||||||
public PrincipalInterest(final Money principal, final Money interest, final Money interestPaymentDueToGrace) {
|
public PrincipalInterest(final Money principal, final Money interest, final Money interestPaymentDueToGrace) {
|
||||||
this.principal = principal;
|
this.principal = principal;
|
||||||
@@ -43,4 +44,12 @@ public class PrincipalInterest {
|
|||||||
public Money interestPaymentDueToGrace() {
|
public Money interestPaymentDueToGrace() {
|
||||||
return this.interestPaymentDueToGrace;
|
return this.interestPaymentDueToGrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money getRescheduleInterestPortion() {
|
||||||
|
return rescheduleInterestPortion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRescheduleInterestPortion(Money rescheduleInterestPortion) {
|
||||||
|
this.rescheduleInterestPortion = rescheduleInterestPortion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -43,4 +43,7 @@ public interface ScheduledDateGenerator {
|
|||||||
|
|
||||||
LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms,
|
LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms,
|
||||||
HolidayDetailDTO holidayDetailDTO);
|
HolidayDetailDTO holidayDetailDTO);
|
||||||
|
|
||||||
|
LocalDate generateNextScheduleDateStartingFromDisburseDateOrRescheduleDate(LocalDate lastRepaymentDate,
|
||||||
|
LoanApplicationTerms loanApplicationTerms, HolidayDetailDTO holidayDetailDTO);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -448,7 +448,10 @@ public class LoanScheduleAssembler {
|
|||||||
Date.from(expectedDisbursementDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), HolidayStatusType.ACTIVE.getValue());
|
Date.from(expectedDisbursementDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), HolidayStatusType.ACTIVE.getValue());
|
||||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI = this.configurationDomainService
|
||||||
|
.isInterestToBeRecoveredFirstWhenGreaterThanEMI();
|
||||||
|
final boolean isPrincipalCompoundingDisabledForOverdueLoans = this.configurationDomainService
|
||||||
|
.isPrincipalCompoundingDisabledForOverdueLoans();
|
||||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
||||||
repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod,
|
repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod,
|
||||||
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||||
@@ -460,7 +463,8 @@ public class LoanScheduleAssembler {
|
|||||||
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||||
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO,
|
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO,
|
||||||
loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipMeetingOnFirstDay, detailDTO,
|
loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipMeetingOnFirstDay, detailDTO,
|
||||||
allowCompoundingOnEod, isEqualAmortization, fixedPrincipalPercentagePerInstallment);
|
allowCompoundingOnEod, isEqualAmortization, isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
||||||
|
fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
||||||
|
|||||||
+5
-3
@@ -144,15 +144,17 @@ public class LoanUtilService {
|
|||||||
boolean isFirstRepaymentDateAllowedOnHoliday = this.configurationDomainService
|
boolean isFirstRepaymentDateAllowedOnHoliday = this.configurationDomainService
|
||||||
.isFirstRepaymentDateAfterRescheduleAllowedOnHoliday();
|
.isFirstRepaymentDateAfterRescheduleAllowedOnHoliday();
|
||||||
|
|
||||||
boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI = this.configurationDomainService
|
boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI = this.configurationDomainService
|
||||||
.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI();
|
.isInterestToBeRecoveredFirstWhenGreaterThanEMI();
|
||||||
|
boolean isPrincipalCompoundingDisabledForOverdueLoans = this.configurationDomainService
|
||||||
|
.isPrincipalCompoundingDisabledForOverdueLoans();
|
||||||
|
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||||
overdurPenaltyWaitPeriod, floatingRateDTO, calendar, calendarHistoryDataWrapper,
|
overdurPenaltyWaitPeriod, floatingRateDTO, calendar, calendarHistoryDataWrapper,
|
||||||
isInterestChargedFromDateAsDisbursementDateEnabled, numberOfDays, isSkipRepaymentOnFirstMonth,
|
isInterestChargedFromDateAsDisbursementDateEnabled, numberOfDays, isSkipRepaymentOnFirstMonth,
|
||||||
isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled, isFirstRepaymentDateAllowedOnHoliday,
|
isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled, isFirstRepaymentDateAllowedOnHoliday,
|
||||||
isInterestToBeAppropriatedEquallyWhenGreaterThanEMI);
|
isInterestToBeRecoveredFirstWhenGreaterThanEMI, isPrincipalCompoundingDisabledForOverdueLoans);
|
||||||
|
|
||||||
return scheduleGeneratorDTO;
|
return scheduleGeneratorDTO;
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
--
|
||||||
|
-- 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.
|
||||||
|
--
|
||||||
|
|
||||||
|
delete from c_configuration where name = 'is-interest-to-be-appropriated-equally-when-greater-than-emi';
|
||||||
|
|
||||||
|
INSERT INTO `c_configuration` (`name`, `value`, `date_value`, `enabled`, `is_trap_door`, `description`)
|
||||||
|
VALUES
|
||||||
|
('is-interest-to-be-recovered-first-when-greater-than-emi', 0, NULL, 0, 0, 'If enabled, when interest amount is greater than EMI, the additional interest is recovered first before princupal');
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `c_configuration` (`name`, `value`, `date_value`, `enabled`, `is_trap_door`, `description`)
|
||||||
|
VALUES
|
||||||
|
('is-principal-compounding-disabled-for-overdue-loans', 0, NULL, 0, 0, 'If enabled, it donot consider principal of an upaid installment for calculating interest of next installment. this is for testing back-dated loan sshedule');
|
||||||
+113
-17
@@ -225,31 +225,31 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enables the configuration `is-interest-to-be-appropriated-equally-when-greater-than-emi`
|
* enables the configuration `is-interest-to-be-recovered-first-when-greater-than-emi`
|
||||||
**/
|
**/
|
||||||
private void enableConfig() {
|
private void enableConfig() {
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "34", true);
|
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "42", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* disables the configuration `is-interest-to-be-appropriated-equally-when-greater-than-emi`
|
* disables the configuration `is-interest-to-be-recovered-first-when-greater-than-emi`
|
||||||
**/
|
**/
|
||||||
private void disableConfig() {
|
private void disableConfig() {
|
||||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "34", false);
|
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "42", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateLoanRescheduleRequestWithInterestAppropriation() {
|
public void testCreateLoanRescheduleRequestWithInterestAppropriation() {
|
||||||
// create all required entities
|
// create all required entities
|
||||||
this.createRequiredEntities();
|
this.createRequiredEntities();
|
||||||
this.createAndApproveLoanRescheduleRequestForInterestAppropriation();
|
this.createAndApproveLoanRescheduleRequestForRecoverInterestInterestFirst();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create new loan reschedule request
|
* create new loan reschedule request
|
||||||
**/
|
**/
|
||||||
private void createAndApproveLoanRescheduleRequestForInterestAppropriation() {
|
private void createAndApproveLoanRescheduleRequestForRecoverInterestInterestFirst() {
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR INTEREST APPROPRIATTION-------------------------------------");
|
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR INTEREST APPROPRIATTION-------------------------------------");
|
||||||
|
|
||||||
@@ -279,8 +279,8 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
|
|||||||
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
||||||
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
||||||
|
|
||||||
assertEquals(12186, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
|
assertEquals(10831, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
|
||||||
assertEquals(123682, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");
|
assertEquals(125184, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");
|
||||||
|
|
||||||
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
|
|||||||
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
||||||
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
||||||
|
|
||||||
assertEquals(12326, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
|
assertEquals(10831, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
|
||||||
assertEquals(131512, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");
|
assertEquals(131512, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");
|
||||||
|
|
||||||
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
||||||
@@ -338,13 +338,13 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
|
|||||||
public void testCreateLoanRescheduleRequestForInterestAppropriationAndFixedEMI() {
|
public void testCreateLoanRescheduleRequestForInterestAppropriationAndFixedEMI() {
|
||||||
// create all required entities
|
// create all required entities
|
||||||
this.createRequiredEntities();
|
this.createRequiredEntities();
|
||||||
this.createAndApproveLoanRescheduleRequestForInterestAppropriationAndFixedEMI();
|
this.createAndApproveLoanRescheduleRequestForRecoverInterestFirstAndFixedEMI();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create new loan reschedule request with combination of date change, interest appropriation and fixed emi
|
* create new loan reschedule request with combination of date change, recover interest first and fixed emi
|
||||||
**/
|
**/
|
||||||
private void createAndApproveLoanRescheduleRequestForInterestAppropriationAndFixedEMI() {
|
private void createAndApproveLoanRescheduleRequestForRecoverInterestFirstAndFixedEMI() {
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR INTEREST APPROPRIATTION-------------------------------------");
|
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR INTEREST APPROPRIATTION-------------------------------------");
|
||||||
|
|
||||||
@@ -379,8 +379,8 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
|
|||||||
|
|
||||||
assertEquals(5000, totalFixedDueForPeriod.intValue(), "EXPECTED FIXED REPAYMENT is NOK");
|
assertEquals(5000, totalFixedDueForPeriod.intValue(), "EXPECTED FIXED REPAYMENT is NOK");
|
||||||
|
|
||||||
assertEquals(15316, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
|
assertEquals(15417, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
|
||||||
assertEquals(120806, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");
|
assertEquals(121412, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");
|
||||||
|
|
||||||
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
||||||
|
|
||||||
@@ -390,7 +390,7 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
|
|||||||
public void testCreateLoanRescheduleRequestWithMultpleInterestAppropriation() {
|
public void testCreateLoanRescheduleRequestWithMultpleInterestAppropriation() {
|
||||||
// create all required entities
|
// create all required entities
|
||||||
this.createRequiredEntities();
|
this.createRequiredEntities();
|
||||||
this.createAndApproveLoanRescheduleRequestForInterestAppropriation();
|
this.createAndApproveLoanRescheduleRequestForRecoverInterestInterestFirst();
|
||||||
|
|
||||||
this.createAndApproveLoanRescheduleRequestForSecondInterestAppropriation();
|
this.createAndApproveLoanRescheduleRequestForSecondInterestAppropriation();
|
||||||
|
|
||||||
@@ -429,9 +429,105 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
|
|||||||
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
||||||
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
||||||
|
|
||||||
assertEquals(12187, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT in Second Reschedule is NOK");
|
assertEquals(10831, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT in Second Reschedule is NOK");
|
||||||
assertEquals(130750, totalExpectedRepayment.intValue(), "TOTAL EXPECTED in Second Reschedule REPAYMENT is NOK");
|
assertEquals(133470, totalExpectedRepayment.intValue(), "TOTAL EXPECTED in Second Reschedule REPAYMENT is NOK");
|
||||||
|
|
||||||
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateLoanInterestGreaterThanEMIFromGapWithRecalculationEnabledAndPrincipalCompoundingOff() {
|
||||||
|
this.enableConfig();
|
||||||
|
this.enablePrincipalCompoundingConfig();
|
||||||
|
// create all required entities
|
||||||
|
this.createRequiredEntitiesWithRecalculationEnabledWithPrincipalCompoundingOff();
|
||||||
|
this.createApproveLoanRescheduleRequestWithRecalculationEnabledWithPrincipalCompoundingOff();
|
||||||
|
this.disablePrincipalCompoundingConfig();
|
||||||
|
this.disableConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createRequiredEntitiesWithRecalculationEnabledWithPrincipalCompoundingOff() {
|
||||||
|
this.createClientEntity();
|
||||||
|
this.createLoanProductWithInterestRecalculation();
|
||||||
|
this.createLoanEntityWithScheduleGapWithInterestGreaterThanEMIAndPrincipalCompoundingOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enables the configuration `is-principal-compounding-disabled-for-overdue-loans`
|
||||||
|
**/
|
||||||
|
private void enablePrincipalCompoundingConfig() {
|
||||||
|
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "43", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* disables the configuration `is-principal-compounding-disabled-for-overdue-loans`
|
||||||
|
**/
|
||||||
|
private void disablePrincipalCompoundingConfig() {
|
||||||
|
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "43", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* submit a new loan application, approve and disburse the loan
|
||||||
|
**/
|
||||||
|
private void createLoanEntityWithScheduleGapWithInterestGreaterThanEMIAndPrincipalCompoundingOff() {
|
||||||
|
String firstRepaymentDate = "01 January 2015";
|
||||||
|
|
||||||
|
LOG.info("---------------------------------NEW LOAN APPLICATION------------------------------------------");
|
||||||
|
|
||||||
|
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("15000").withLoanTermFrequency("24")
|
||||||
|
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("24").withRepaymentEveryAfter("1")
|
||||||
|
.withRepaymentFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestCalculationPeriodTypeAsDays()
|
||||||
|
.withInterestRatePerPeriod("25").withInterestTypeAsDecliningBalance().withSubmittedOnDate(this.dateString)
|
||||||
|
.withExpectedDisbursementDate(this.dateString).withFirstRepaymentDate(firstRepaymentDate)
|
||||||
|
.withinterestChargedFromDate(this.dateString).build(this.clientId.toString(), this.loanProductId.toString(), null);
|
||||||
|
|
||||||
|
this.loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||||
|
|
||||||
|
LOG.info("Sucessfully created loan (ID: {} )", this.loanId);
|
||||||
|
|
||||||
|
this.approveLoanApplication();
|
||||||
|
this.disburseLoan();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create new loan reschedule request with recalculation enabled in Loan product
|
||||||
|
**/
|
||||||
|
|
||||||
|
private void createApproveLoanRescheduleRequestWithRecalculationEnabledWithPrincipalCompoundingOff() {
|
||||||
|
LOG.info(
|
||||||
|
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR LOAN WITH RECALCULATION------------------------------------");
|
||||||
|
|
||||||
|
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnPrincipal(null).updateGraceOnInterest(null)
|
||||||
|
.updateExtraTerms(null).updateRescheduleFromDate("01 March 2015").updateAdjustedDueDate("01 July 2015")
|
||||||
|
.updateRecalculateInterest(true).build(this.loanId.toString());
|
||||||
|
|
||||||
|
this.loanRescheduleRequestId = this.loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
|
||||||
|
this.loanRescheduleRequestHelper.verifyCreationOfLoanRescheduleRequest(this.loanRescheduleRequestId);
|
||||||
|
|
||||||
|
LOG.info("Successfully created loan reschedule request (ID: {} )", this.loanRescheduleRequestId);
|
||||||
|
|
||||||
|
final String aproveRequestJSON = new LoanRescheduleRequestTestBuilder().getApproveLoanRescheduleRequestJSON();
|
||||||
|
this.loanRescheduleRequestHelper.approveLoanRescheduleRequest(this.loanRescheduleRequestId, aproveRequestJSON);
|
||||||
|
final HashMap response = (HashMap) this.loanRescheduleRequestHelper.getLoanRescheduleRequest(loanRescheduleRequestId, "statusEnum");
|
||||||
|
assertTrue((Boolean) response.get("approved"));
|
||||||
|
|
||||||
|
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
||||||
|
|
||||||
|
final Map repaymentSchedule = (Map) this.loanTransactionHelper.getLoanDetail(requestSpec, generalResponseSpec, loanId,
|
||||||
|
"repaymentSchedule");
|
||||||
|
final ArrayList periods = (ArrayList) repaymentSchedule.get("periods");
|
||||||
|
|
||||||
|
HashMap period = (HashMap) periods.get(5);
|
||||||
|
Float totalDueForPeriod = (Float) period.get("totalDueForPeriod");
|
||||||
|
|
||||||
|
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
|
||||||
|
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
|
||||||
|
|
||||||
|
assertEquals(798, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
|
||||||
|
assertEquals(22567, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");
|
||||||
|
|
||||||
|
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-10
@@ -90,8 +90,8 @@ public class GlobalConfigurationHelper {
|
|||||||
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
|
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
|
||||||
|
|
||||||
// There are currently 37 global configurations.
|
// There are currently 37 global configurations.
|
||||||
Assertions.assertEquals(37, expectedGlobalConfigurations.size());
|
Assertions.assertEquals(38, expectedGlobalConfigurations.size());
|
||||||
Assertions.assertEquals(37, actualGlobalConfigurations.size());
|
Assertions.assertEquals(38, actualGlobalConfigurations.size());
|
||||||
|
|
||||||
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
|
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
|
||||||
|
|
||||||
@@ -355,14 +355,6 @@ public class GlobalConfigurationHelper {
|
|||||||
isFirstPaydayAllowedOnHoliday.put("trapDoor", false);
|
isFirstPaydayAllowedOnHoliday.put("trapDoor", false);
|
||||||
defaults.add(isFirstPaydayAllowedOnHoliday);
|
defaults.add(isFirstPaydayAllowedOnHoliday);
|
||||||
|
|
||||||
HashMap<String, Object> isInterestAppropriationEnabled = new HashMap<>();
|
|
||||||
isInterestAppropriationEnabled.put("id", 34);
|
|
||||||
isInterestAppropriationEnabled.put("name", "is-interest-to-be-appropriated-equally-when-greater-than-emi");
|
|
||||||
isInterestAppropriationEnabled.put("value", 0);
|
|
||||||
isInterestAppropriationEnabled.put("enabled", false);
|
|
||||||
isInterestAppropriationEnabled.put("trapDoor", false);
|
|
||||||
defaults.add(isInterestAppropriationEnabled);
|
|
||||||
|
|
||||||
HashMap<String, Object> isAccountMappedForPayment = new HashMap<>();
|
HashMap<String, Object> isAccountMappedForPayment = new HashMap<>();
|
||||||
isAccountMappedForPayment.put("id", 35);
|
isAccountMappedForPayment.put("id", 35);
|
||||||
isAccountMappedForPayment.put("name", "account-mapping-for-payment-type");
|
isAccountMappedForPayment.put("name", "account-mapping-for-payment-type");
|
||||||
@@ -423,6 +415,22 @@ public class GlobalConfigurationHelper {
|
|||||||
isAccountNumberRandomGenerated.put("trapDoor", false);
|
isAccountNumberRandomGenerated.put("trapDoor", false);
|
||||||
defaults.add(isAccountNumberRandomGenerated);
|
defaults.add(isAccountNumberRandomGenerated);
|
||||||
|
|
||||||
|
HashMap<String, Object> isInterestAppropriationEnabled = new HashMap<>();
|
||||||
|
isInterestAppropriationEnabled.put("id", 42);
|
||||||
|
isInterestAppropriationEnabled.put("name", "is-interest-to-be-recovered-first-when-greater-than-emi");
|
||||||
|
isInterestAppropriationEnabled.put("value", 0);
|
||||||
|
isInterestAppropriationEnabled.put("enabled", false);
|
||||||
|
isInterestAppropriationEnabled.put("trapDoor", false);
|
||||||
|
defaults.add(isInterestAppropriationEnabled);
|
||||||
|
|
||||||
|
HashMap<String, Object> isPrincipalCompoundingDisabled = new HashMap<>();
|
||||||
|
isPrincipalCompoundingDisabled.put("id", 43);
|
||||||
|
isPrincipalCompoundingDisabled.put("name", "is-principal-compounding-disabled-for-overdue-loans");
|
||||||
|
isPrincipalCompoundingDisabled.put("value", 0);
|
||||||
|
isPrincipalCompoundingDisabled.put("enabled", false);
|
||||||
|
isPrincipalCompoundingDisabled.put("trapDoor", false);
|
||||||
|
defaults.add(isPrincipalCompoundingDisabled);
|
||||||
|
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
@@ -77,6 +77,7 @@ public class LoanApplicationTestBuilder {
|
|||||||
private List<HashMap<String, Object>> datatables = null;
|
private List<HashMap<String, Object>> datatables = null;
|
||||||
private List<Map<String, Object>> approvalFormData = null;
|
private List<Map<String, Object>> approvalFormData = null;
|
||||||
private String fixedPrincipalPercentagePerInstallment;
|
private String fixedPrincipalPercentagePerInstallment;
|
||||||
|
private String interestChargedFromDate;
|
||||||
|
|
||||||
public String build(final String clientID, final String groupID, final String loanProductId, final String savingsID) {
|
public String build(final String clientID, final String groupID, final String loanProductId, final String savingsID) {
|
||||||
final HashMap<String, Object> map = new HashMap<>();
|
final HashMap<String, Object> map = new HashMap<>();
|
||||||
@@ -150,6 +151,7 @@ public class LoanApplicationTestBuilder {
|
|||||||
map.put("submittedOnDate", this.submittedOnDate);
|
map.put("submittedOnDate", this.submittedOnDate);
|
||||||
map.put("loanType", this.loanType);
|
map.put("loanType", this.loanType);
|
||||||
map.put("collateral", this.collaterals);
|
map.put("collateral", this.collaterals);
|
||||||
|
map.put("interestChargedFromDate", this.interestChargedFromDate);
|
||||||
|
|
||||||
if (repaymentsStartingFromDate != null) {
|
if (repaymentsStartingFromDate != null) {
|
||||||
map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate);
|
map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate);
|
||||||
@@ -382,4 +384,9 @@ public class LoanApplicationTestBuilder {
|
|||||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanApplicationTestBuilder withinterestChargedFromDate(String interestChargedFromDate) {
|
||||||
|
this.interestChargedFromDate = interestChargedFromDate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user