MIFOSX-1844 : added config to manage last transaction
This commit is contained in:
+14
-4
@@ -11397,7 +11397,14 @@ Request Body:
|
|||||||
<td class=fielddesc>The minimum number of days allowed between a Loan disbursal and its first repayment.
|
<td class=fielddesc>The minimum number of days allowed between a Loan disbursal and its first repayment.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>principalThresholdForLastInstalment</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Field represents percentage of current instalment principal amount for comparing against principal outstanding to add another repayment instalment. If the outstanding principal amount is less then calculated amount, remaining outstanding amount will be added to current instalment. Default value for multi disburse loan is 50% and non-multi disburse loan is 0%
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -11641,7 +11648,8 @@ GET https://DomainName/api/v1/loanproducts
|
|||||||
"id": 2,
|
"id": 2,
|
||||||
"code": "accountingRuleType.cash",
|
"code": "accountingRuleType.cash",
|
||||||
"value": "CASH BASED"
|
"value": "CASH BASED"
|
||||||
}
|
},
|
||||||
|
"principalThresholdForLastInstalment":0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
</code>
|
</code>
|
||||||
@@ -12719,7 +12727,8 @@ GET https://DomainName/api/v1/loanproducts/{productId}
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"multiDisburseLoan":true,"maxTrancheCount":3,"outstandingLoanBalance":36000.000000,
|
"multiDisburseLoan":true,"maxTrancheCount":3,"outstandingLoanBalance":36000.000000,
|
||||||
"overdueDaysForNPA":2
|
"overdueDaysForNPA":2,
|
||||||
|
"principalThresholdForLastInstalment":50
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
@@ -12763,7 +12772,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
|
|||||||
numberOfRepaymentVariationsForBorrowerCycle,
|
numberOfRepaymentVariationsForBorrowerCycle,
|
||||||
interestRateVariationsForBorrowerCycle,
|
interestRateVariationsForBorrowerCycle,
|
||||||
multiDisburseLoan,maxTrancheCount,
|
multiDisburseLoan,maxTrancheCount,
|
||||||
outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds
|
outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds, principalThresholdForLastInstalment
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -12883,7 +12892,8 @@ Request Body:
|
|||||||
"daysInMonthType":1,
|
"daysInMonthType":1,
|
||||||
"daysInYearType":1,
|
"daysInYearType":1,
|
||||||
"isInterestRecalculationEnabled":"false",
|
"isInterestRecalculationEnabled":"false",
|
||||||
"holdGuaranteeFunds":"false"
|
"holdGuaranteeFunds":"false",
|
||||||
|
"principalThresholdForLastInstalment":20
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
<code class="method-response">
|
<code class="method-response">
|
||||||
|
|||||||
+9
-5
@@ -1928,7 +1928,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, approvedLoanAmount);
|
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, approvedLoanAmount);
|
||||||
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, approvedLoanAmount);
|
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, approvedLoanAmount);
|
||||||
|
|
||||||
} else if(approvedLoanAmount.compareTo(this.proposedPrincipal) == 1){
|
} else if (approvedLoanAmount.compareTo(this.proposedPrincipal) == 1) {
|
||||||
final String errorMessage = "Loan approved amount can't be greater than loan amount demanded.";
|
final String errorMessage = "Loan approved amount can't be greater than loan amount demanded.";
|
||||||
throw new InvalidLoanStateTransitionException("approval", "amount.can't.be.greater.than.loan.amount.demanded",
|
throw new InvalidLoanStateTransitionException("approval", "amount.can't.be.greater.than.loan.amount.demanded",
|
||||||
errorMessage, this.proposedPrincipal, approvedLoanAmount);
|
errorMessage, this.proposedPrincipal, approvedLoanAmount);
|
||||||
@@ -2316,7 +2316,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(),
|
loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(),
|
||||||
getExpectedFirstRepaymentOnDate(), scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(),
|
getExpectedFirstRepaymentOnDate(), scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(),
|
||||||
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
|
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
|
||||||
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
|
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
|
||||||
|
this.loanProduct.getPrincipalThresholdForLastInstalment());
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||||
@@ -4649,7 +4650,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||||
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
||||||
loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails,
|
loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails,
|
||||||
calendarInstanceForInterestRecalculation, recalculationFrequencyType);
|
calendarInstanceForInterestRecalculation, recalculationFrequencyType,
|
||||||
|
this.loanProduct.getPrincipalThresholdForLastInstalment());
|
||||||
return loanApplicationTerms;
|
return loanApplicationTerms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4692,7 +4694,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(null, loanTermFrequency,
|
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(null, loanTermFrequency,
|
||||||
loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), null,
|
loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), null,
|
||||||
getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount,
|
getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount,
|
||||||
disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
|
disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
|
||||||
|
this.loanProduct.getPrincipalThresholdForLastInstalment());
|
||||||
|
|
||||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
||||||
LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc, charges());
|
LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc, charges());
|
||||||
@@ -4893,7 +4896,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
|
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
|
||||||
dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||||
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData,
|
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData,
|
||||||
maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate);
|
maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate,
|
||||||
|
this.loanProduct.getPrincipalThresholdForLastInstalment());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+20
-11
@@ -132,6 +132,8 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
private final RecalculationFrequencyType recalculationFrequencyType;
|
private final RecalculationFrequencyType recalculationFrequencyType;
|
||||||
|
|
||||||
|
private final BigDecimal principalThresholdForLastInstalment;
|
||||||
|
|
||||||
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,
|
||||||
@@ -144,7 +146,8 @@ public final class LoanApplicationTerms {
|
|||||||
final Money inArrearsTolerance, final boolean multiDisburseLoan, final BigDecimal emiAmount,
|
final Money inArrearsTolerance, final boolean multiDisburseLoan, final BigDecimal emiAmount,
|
||||||
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
||||||
final List<LoanTermVariationsData> emiAmountVariations, final Integer graceOnArrearsAgeing,
|
final List<LoanTermVariationsData> emiAmountVariations, final Integer graceOnArrearsAgeing,
|
||||||
final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled) {
|
final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||||
|
BigDecimal principalThresholdForLastInstalment) {
|
||||||
|
|
||||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
||||||
@@ -157,7 +160,7 @@ public final class LoanApplicationTerms {
|
|||||||
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
|
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
|
||||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
||||||
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
|
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
|
||||||
recalculationFrequencyType);
|
recalculationFrequencyType, principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||||
@@ -166,7 +169,8 @@ public final class LoanApplicationTerms {
|
|||||||
final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
|
final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
|
||||||
final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
|
final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
|
||||||
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
||||||
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate) {
|
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
||||||
|
final BigDecimal principalThresholdForLastInstalment) {
|
||||||
|
|
||||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||||
@@ -201,7 +205,8 @@ public final class LoanApplicationTerms {
|
|||||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance,
|
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance,
|
||||||
multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
|
multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
|
||||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType);
|
rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType,
|
||||||
|
principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||||
@@ -211,7 +216,7 @@ public final class LoanApplicationTerms {
|
|||||||
final BigDecimal emiAmount, final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
final BigDecimal emiAmount, final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
|
||||||
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
||||||
final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance,
|
final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance,
|
||||||
final RecalculationFrequencyType recalculationFrequencyType) {
|
final RecalculationFrequencyType recalculationFrequencyType, final BigDecimal principalThresholdForLastInstalment) {
|
||||||
|
|
||||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||||
@@ -248,7 +253,8 @@ public final class LoanApplicationTerms {
|
|||||||
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
|
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
|
||||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(),
|
disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(),
|
||||||
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
|
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
|
||||||
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType);
|
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType,
|
||||||
|
principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||||
@@ -266,7 +272,7 @@ public final class LoanApplicationTerms {
|
|||||||
applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType, applicationTerms.daysInYearType,
|
applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType, applicationTerms.daysInYearType,
|
||||||
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
|
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
|
||||||
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
|
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
|
||||||
applicationTerms.recalculationFrequencyType);
|
applicationTerms.recalculationFrequencyType, applicationTerms.principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
@@ -283,7 +289,8 @@ public final class LoanApplicationTerms {
|
|||||||
final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
|
final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
|
||||||
final boolean isInterestRecalculationEnabled, final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
|
final boolean isInterestRecalculationEnabled, final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
|
||||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
|
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
|
||||||
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType) {
|
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
|
||||||
|
final BigDecimal principalThresholdForLastInstalment) {
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.loanTermFrequency = loanTermFrequency;
|
this.loanTermFrequency = loanTermFrequency;
|
||||||
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
||||||
@@ -324,6 +331,7 @@ public final class LoanApplicationTerms {
|
|||||||
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
|
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
|
||||||
this.restCalendarInstance = restCalendarInstance;
|
this.restCalendarInstance = restCalendarInstance;
|
||||||
this.recalculationFrequencyType = recalculationFrequencyType;
|
this.recalculationFrequencyType = recalculationFrequencyType;
|
||||||
|
this.principalThresholdForLastInstalment = principalThresholdForLastInstalment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||||
@@ -336,10 +344,11 @@ public final class LoanApplicationTerms {
|
|||||||
// paid too much principal, subtract amount that overpays from
|
// paid too much principal, subtract amount that overpays from
|
||||||
// principal paid for period.
|
// principal paid for period.
|
||||||
adjusted = principalForPeriod.minus(totalPrincipalRemaining.abs());
|
adjusted = principalForPeriod.minus(totalPrincipalRemaining.abs());
|
||||||
} else if (multiDisburseLoan) {
|
} else if (this.fixedEmiAmount != null) {
|
||||||
final Money difference = this.principal.minus(totalCumulativePrincipalToDate);
|
final Money difference = this.principal.minus(totalCumulativePrincipalToDate);
|
||||||
final Money halfPrincipalVal = principalForPeriod.dividedBy(2, RoundingMode.HALF_DOWN);
|
final Money principalThreshold = principalForPeriod.multipliedBy(this.principalThresholdForLastInstalment).dividedBy(100,
|
||||||
if (difference.isLessThan(halfPrincipalVal)) {
|
RoundingMode.HALF_EVEN);
|
||||||
|
if (difference.isLessThan(principalThreshold)) {
|
||||||
adjusted = principalForPeriod.plus(difference.abs());
|
adjusted = principalForPeriod.plus(difference.abs());
|
||||||
}
|
}
|
||||||
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
} else if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
|
||||||
|
|||||||
+3
-1
@@ -249,6 +249,8 @@ public class LoanScheduleAssembler {
|
|||||||
final DaysInYearType daysInYearType = loanProduct.fetchDaysInYearType();
|
final DaysInYearType daysInYearType = loanProduct.fetchDaysInYearType();
|
||||||
|
|
||||||
final boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled();
|
final boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled();
|
||||||
|
|
||||||
|
final BigDecimal principalThresholdForLastInstalment = loanProduct.getPrincipalThresholdForLastInstalment();
|
||||||
|
|
||||||
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,
|
||||||
@@ -256,7 +258,7 @@ public class LoanScheduleAssembler {
|
|||||||
principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney,
|
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney,
|
||||||
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData,
|
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData,
|
||||||
graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled);
|
graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DisbursementData> fetchDisbursementData(final JsonObject command) {
|
private List<DisbursementData> fetchDisbursementData(final JsonObject command) {
|
||||||
|
|||||||
+5
@@ -5,6 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanproduct;
|
package org.mifosplatform.portfolio.loanproduct;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
public interface LoanProductConstants {
|
public interface LoanProductConstants {
|
||||||
|
|
||||||
public static final String useBorrowerCycleParameterName = "useBorrowerCycle";
|
public static final String useBorrowerCycleParameterName = "useBorrowerCycle";
|
||||||
@@ -78,4 +80,7 @@ public interface LoanProductConstants {
|
|||||||
public static final String minimumGuaranteeFromOwnFundsParamName = "minimumGuaranteeFromOwnFunds";
|
public static final String minimumGuaranteeFromOwnFundsParamName = "minimumGuaranteeFromOwnFunds";
|
||||||
public static final String minimumGuaranteeFromGuarantorParamName = "minimumGuaranteeFromGuarantor";
|
public static final String minimumGuaranteeFromGuarantorParamName = "minimumGuaranteeFromGuarantor";
|
||||||
|
|
||||||
|
public static final String principalThresholdForLastInstalmentParamName = "principalThresholdForLastInstalment";
|
||||||
|
public static final BigDecimal DEFAULT_PRINCIPAL_THRESHOLD_FOR_MULTI_DISBURSE_LOAN = BigDecimal.valueOf(50);
|
||||||
|
public static final BigDecimal DEFAULT_PRINCIPAL_THRESHOLD_FOR_SINGLE_DISBURSE_LOAN = BigDecimal.valueOf(0);
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -134,6 +134,7 @@ public class LoanProductData {
|
|||||||
private final Boolean multiDisburseLoan;
|
private final Boolean multiDisburseLoan;
|
||||||
private final Integer maxTrancheCount;
|
private final Integer maxTrancheCount;
|
||||||
private final BigDecimal outstandingLoanBalance;
|
private final BigDecimal outstandingLoanBalance;
|
||||||
|
private final BigDecimal principalThresholdForLastInstalment;
|
||||||
|
|
||||||
private final Boolean holdGuaranteeFunds;
|
private final Boolean holdGuaranteeFunds;
|
||||||
private final LoanProductGuaranteeData productGuaranteeData;
|
private final LoanProductGuaranteeData productGuaranteeData;
|
||||||
@@ -187,6 +188,7 @@ public class LoanProductData {
|
|||||||
final BigDecimal outstandingLoanBalance = null;
|
final BigDecimal outstandingLoanBalance = null;
|
||||||
final LoanProductGuaranteeData productGuaranteeData = null;
|
final LoanProductGuaranteeData productGuaranteeData = null;
|
||||||
final Boolean holdGuaranteeFunds = false;
|
final Boolean holdGuaranteeFunds = false;
|
||||||
|
final BigDecimal principalThresholdForLastInstalment = null;
|
||||||
|
|
||||||
final EnumOptionData daysInMonthType = null;
|
final EnumOptionData daysInMonthType = null;
|
||||||
final EnumOptionData daysInYearType = null;
|
final EnumOptionData daysInYearType = null;
|
||||||
@@ -202,7 +204,8 @@ public class LoanProductData {
|
|||||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
||||||
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
||||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData);
|
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
|
||||||
|
principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
|
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
|
||||||
@@ -258,6 +261,7 @@ public class LoanProductData {
|
|||||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
||||||
final Boolean holdGuaranteeFunds = false;
|
final Boolean holdGuaranteeFunds = false;
|
||||||
final LoanProductGuaranteeData productGuaranteeData = null;
|
final LoanProductGuaranteeData productGuaranteeData = null;
|
||||||
|
final BigDecimal principalThresholdForLastInstalment = null;
|
||||||
|
|
||||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||||
@@ -267,7 +271,8 @@ public class LoanProductData {
|
|||||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
||||||
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
interestRateVariations, numberOfRepaymentVariations, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
||||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData);
|
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
|
||||||
|
principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
|
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
|
||||||
@@ -330,6 +335,7 @@ public class LoanProductData {
|
|||||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
||||||
final Boolean holdGuaranteeFunds = false;
|
final Boolean holdGuaranteeFunds = false;
|
||||||
final LoanProductGuaranteeData productGuaranteeData = null;
|
final LoanProductGuaranteeData productGuaranteeData = null;
|
||||||
|
final BigDecimal principalThresholdForLastInstalment = null;
|
||||||
|
|
||||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||||
@@ -340,7 +346,7 @@ public class LoanProductData {
|
|||||||
principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle,
|
principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle,
|
||||||
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType,
|
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType,
|
||||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment,
|
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment,
|
||||||
holdGuaranteeFunds, productGuaranteeData);
|
holdGuaranteeFunds, productGuaranteeData, principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
||||||
@@ -372,7 +378,7 @@ public class LoanProductData {
|
|||||||
final Integer overdueDaysForNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType,
|
final Integer overdueDaysForNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType,
|
||||||
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData,
|
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData,
|
||||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds,
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds,
|
||||||
LoanProductGuaranteeData loanProductGuaranteeData) {
|
final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstalment) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
@@ -445,6 +451,7 @@ public class LoanProductData {
|
|||||||
this.interestRecalculationData = interestRecalculationData;
|
this.interestRecalculationData = interestRecalculationData;
|
||||||
this.holdGuaranteeFunds = holdGuaranteeFunds;
|
this.holdGuaranteeFunds = holdGuaranteeFunds;
|
||||||
this.productGuaranteeData = loanProductGuaranteeData;
|
this.productGuaranteeData = loanProductGuaranteeData;
|
||||||
|
this.principalThresholdForLastInstalment = principalThresholdForLastInstalment;
|
||||||
|
|
||||||
this.daysInMonthTypeOptions = null;
|
this.daysInMonthTypeOptions = null;
|
||||||
this.daysInYearTypeOptions = null;
|
this.daysInYearTypeOptions = null;
|
||||||
@@ -548,6 +555,7 @@ public class LoanProductData {
|
|||||||
this.interestRecalculationData = productData.interestRecalculationData;
|
this.interestRecalculationData = productData.interestRecalculationData;
|
||||||
this.holdGuaranteeFunds = productData.holdGuaranteeFunds;
|
this.holdGuaranteeFunds = productData.holdGuaranteeFunds;
|
||||||
this.productGuaranteeData = productData.productGuaranteeData;
|
this.productGuaranteeData = productData.productGuaranteeData;
|
||||||
|
this.principalThresholdForLastInstalment = productData.principalThresholdForLastInstalment;
|
||||||
|
|
||||||
this.daysInMonthTypeOptions = daysInMonthTypeOptions;
|
this.daysInMonthTypeOptions = daysInMonthTypeOptions;
|
||||||
this.daysInYearTypeOptions = daysInYearTypeOptions;
|
this.daysInYearTypeOptions = daysInYearTypeOptions;
|
||||||
|
|||||||
+26
-2
@@ -136,6 +136,9 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
|
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
|
||||||
private LoanProductGuaranteeDetails loanProductGuaranteeDetails;
|
private LoanProductGuaranteeDetails loanProductGuaranteeDetails;
|
||||||
|
|
||||||
|
@Column(name = "principal_threshold_for_last_instalment", scale = 2, precision = 5, nullable = false)
|
||||||
|
private BigDecimal principalThresholdForLastInstalment;
|
||||||
|
|
||||||
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
||||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
|
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
|
||||||
|
|
||||||
@@ -225,6 +228,14 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
|
loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BigDecimal principalThresholdForLastInstalment = command
|
||||||
|
.bigDecimalValueOfParameterNamed(LoanProductConstants.principalThresholdForLastInstalmentParamName);
|
||||||
|
|
||||||
|
if (principalThresholdForLastInstalment == null) {
|
||||||
|
principalThresholdForLastInstalment = multiDisburseLoan ? LoanProductConstants.DEFAULT_PRINCIPAL_THRESHOLD_FOR_MULTI_DISBURSE_LOAN
|
||||||
|
: LoanProductConstants.DEFAULT_PRINCIPAL_THRESHOLD_FOR_SINGLE_DISBURSE_LOAN;
|
||||||
|
}
|
||||||
|
|
||||||
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal,
|
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal,
|
||||||
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType,
|
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType,
|
||||||
annualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType,
|
annualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType,
|
||||||
@@ -233,7 +244,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
startDate, closeDate, externalId, useBorrowerCycle, loanProductBorrowerCycleVariations, multiDisburseLoan, maxTrancheCount,
|
startDate, closeDate, externalId, useBorrowerCycle, loanProductBorrowerCycleVariations, multiDisburseLoan, maxTrancheCount,
|
||||||
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
|
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
|
||||||
isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment,
|
isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment,
|
||||||
holdGuarantorFunds, loanProductGuaranteeDetails);
|
holdGuarantorFunds, loanProductGuaranteeDetails, principalThresholdForLastInstalment);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +469,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
final boolean isInterestRecalculationEnabled,
|
final boolean isInterestRecalculationEnabled,
|
||||||
final LoanProductInterestRecalculationDetails productInterestRecalculationDetails,
|
final LoanProductInterestRecalculationDetails productInterestRecalculationDetails,
|
||||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds,
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds,
|
||||||
final LoanProductGuaranteeDetails loanProductGuaranteeDetails) {
|
final LoanProductGuaranteeDetails loanProductGuaranteeDetails, final BigDecimal principalThresholdForLastInstalment) {
|
||||||
this.fund = fund;
|
this.fund = fund;
|
||||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||||
this.name = name.trim();
|
this.name = name.trim();
|
||||||
@@ -510,6 +521,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment;
|
this.minimumDaysBetweenDisbursalAndFirstRepayment = minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
this.holdGuaranteeFunds = holdGuarantorFunds;
|
this.holdGuaranteeFunds = holdGuarantorFunds;
|
||||||
this.loanProductGuaranteeDetails = loanProductGuaranteeDetails;
|
this.loanProductGuaranteeDetails = loanProductGuaranteeDetails;
|
||||||
|
this.principalThresholdForLastInstalment = principalThresholdForLastInstalment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonetaryCurrency getCurrency() {
|
public MonetaryCurrency getCurrency() {
|
||||||
@@ -737,6 +749,14 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
this.loanProductGuaranteeDetails.update(command, actualChanges);
|
this.loanProductGuaranteeDetails.update(command, actualChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInBigDecimalParameterNamed(LoanProductConstants.principalThresholdForLastInstalmentParamName,
|
||||||
|
this.principalThresholdForLastInstalment)) {
|
||||||
|
BigDecimal newValue = command
|
||||||
|
.bigDecimalValueOfParameterNamed(LoanProductConstants.principalThresholdForLastInstalmentParamName);
|
||||||
|
actualChanges.put(LoanProductConstants.principalThresholdForLastInstalmentParamName, newValue);
|
||||||
|
this.principalThresholdForLastInstalment = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
return actualChanges;
|
return actualChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -989,4 +1009,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
return this.shortName;
|
return this.shortName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigDecimal getPrincipalThresholdForLastInstalment() {
|
||||||
|
return this.principalThresholdForLastInstalment;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+14
-1
@@ -73,7 +73,7 @@ public final class LoanProductDataValidator {
|
|||||||
LoanProductConstants.recalculationRestFrequencyTypeParameterName,
|
LoanProductConstants.recalculationRestFrequencyTypeParameterName,
|
||||||
LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, LoanProductConstants.mandatoryGuaranteeParamName,
|
LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, LoanProductConstants.mandatoryGuaranteeParamName,
|
||||||
LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName,
|
LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName,
|
||||||
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName));
|
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstalmentParamName));
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
@@ -325,6 +325,11 @@ public final class LoanProductDataValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BigDecimal principalThresholdForLastInstalment = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||||
|
LoanProductConstants.principalThresholdForLastInstalmentParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.principalThresholdForLastInstalmentParamName)
|
||||||
|
.value(principalThresholdForLastInstalment).notLessThanMin(BigDecimal.ZERO).notGreaterThanMax(BigDecimal.valueOf(100));
|
||||||
|
|
||||||
// accounting related data validation
|
// accounting related data validation
|
||||||
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
|
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 4);
|
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 4);
|
||||||
@@ -748,6 +753,14 @@ public final class LoanProductDataValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.principalThresholdForLastInstalmentParamName, element)) {
|
||||||
|
BigDecimal principalThresholdForLastInstalment = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||||
|
LoanProductConstants.principalThresholdForLastInstalmentParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.principalThresholdForLastInstalmentParamName)
|
||||||
|
.value(principalThresholdForLastInstalment).notNull().notLessThanMin(BigDecimal.ZERO)
|
||||||
|
.notGreaterThanMax(BigDecimal.valueOf(100));
|
||||||
|
}
|
||||||
|
|
||||||
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
|
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).ignoreIfNull().inMinMaxRange(1, 4);
|
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).ignoreIfNull().inMinMaxRange(1, 4);
|
||||||
|
|
||||||
|
|||||||
+56
-54
@@ -86,13 +86,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
|
|
||||||
String sql = "select " + rm.loanProductSchema();
|
String sql = "select " + rm.loanProductSchema();
|
||||||
|
|
||||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
// Check if branch specific products are enabled. If yes, fetch only
|
||||||
String inClause = mifosEntityAccessUtil.
|
// products mapped to current user's office
|
||||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
String inClause = mifosEntityAccessUtil
|
||||||
MifosEntityType.LOAN_PRODUCT);
|
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(MifosEntityType.LOAN_PRODUCT);
|
||||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||||
sql += " where lp.id in ( " + inClause + " ) ";
|
sql += " where lp.id in ( " + inClause + " ) ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
||||||
}
|
}
|
||||||
@@ -109,18 +109,18 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
final LoanProductLookupMapper rm = new LoanProductLookupMapper();
|
final LoanProductLookupMapper rm = new LoanProductLookupMapper();
|
||||||
|
|
||||||
String sql = "select ";
|
String sql = "select ";
|
||||||
if(activeOnly) {
|
if (activeOnly) {
|
||||||
sql += rm.activeOnlySchema();
|
sql += rm.activeOnlySchema();
|
||||||
} else {
|
} else {
|
||||||
sql += rm.schema();
|
sql += rm.schema();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
// Check if branch specific products are enabled. If yes, fetch only
|
||||||
String inClause = mifosEntityAccessUtil.
|
// products mapped to current user's office
|
||||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
String inClause = mifosEntityAccessUtil
|
||||||
MifosEntityType.LOAN_PRODUCT);
|
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(MifosEntityType.LOAN_PRODUCT);
|
||||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||||
if(activeOnly) {
|
if (activeOnly) {
|
||||||
sql += " and id in ( " + inClause + " )";
|
sql += " and id in ( " + inClause + " )";
|
||||||
} else {
|
} else {
|
||||||
sql += " where id in ( " + inClause + " ) ";
|
sql += " where id in ( " + inClause + " ) ";
|
||||||
@@ -164,6 +164,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
+ "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, "
|
+ "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, "
|
||||||
+ "lpr.rest_freqency_date as restFrequencyDate, "
|
+ "lpr.rest_freqency_date as restFrequencyDate, "
|
||||||
+ "lp.hold_guarantee_funds as holdGuaranteeFunds, "
|
+ "lp.hold_guarantee_funds as holdGuaranteeFunds, "
|
||||||
|
+ "lp.principal_threshold_for_last_instalment as principalThresholdForLastInstalment, "
|
||||||
+ "lpg.id as lpgId, lpg.mandatory_guarantee as mandatoryGuarantee, "
|
+ "lpg.id as lpgId, lpg.mandatory_guarantee as mandatoryGuarantee, "
|
||||||
+ "lpg.minimum_guarantee_from_own_funds as minimumGuaranteeFromOwnFunds, lpg.minimum_guarantee_from_guarantor_funds as minimumGuaranteeFromGuarantor, "
|
+ "lpg.minimum_guarantee_from_own_funds as minimumGuaranteeFromOwnFunds, lpg.minimum_guarantee_from_guarantor_funds as minimumGuaranteeFromGuarantor, "
|
||||||
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId "
|
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId "
|
||||||
@@ -307,6 +308,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
minimumGuaranteeFromGuarantor);
|
minimumGuaranteeFromGuarantor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final BigDecimal principalThresholdForLastInstalment = rs.getBigDecimal("principalThresholdForLastInstalment");
|
||||||
|
|
||||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||||
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
|
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
|
||||||
@@ -316,7 +319,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
closeDate, status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle,
|
closeDate, status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle,
|
||||||
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
|
||||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData);
|
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData,
|
||||||
|
principalThresholdForLastInstalment);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -390,13 +394,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
|
|
||||||
String sql = "select " + rm.loanProductSchema() + " where lp.currency_code='" + currencyCode + "'";
|
String sql = "select " + rm.loanProductSchema() + " where lp.currency_code='" + currencyCode + "'";
|
||||||
|
|
||||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
// Check if branch specific products are enabled. If yes, fetch only
|
||||||
String inClause = mifosEntityAccessUtil.
|
// products mapped to current user's office
|
||||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
String inClause = mifosEntityAccessUtil
|
||||||
MifosEntityType.LOAN_PRODUCT);
|
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(MifosEntityType.LOAN_PRODUCT);
|
||||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||||
sql += " and id in ( " + inClause + " ) ";
|
sql += " and id in ( " + inClause + " ) ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
||||||
}
|
}
|
||||||
@@ -410,13 +414,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
|
|
||||||
String sql = "Select " + rm.productMixSchema();
|
String sql = "Select " + rm.productMixSchema();
|
||||||
|
|
||||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
// Check if branch specific products are enabled. If yes, fetch only
|
||||||
String inClause = mifosEntityAccessUtil.
|
// products mapped to current user's office
|
||||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
String inClause = mifosEntityAccessUtil
|
||||||
MifosEntityType.LOAN_PRODUCT);
|
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(MifosEntityType.LOAN_PRODUCT);
|
||||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||||
sql += " and lp.id in ( " + inClause + " ) ";
|
sql += " and lp.id in ( " + inClause + " ) ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
||||||
}
|
}
|
||||||
@@ -429,24 +433,23 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
final LoanProductLookupMapper rm = new LoanProductLookupMapper();
|
final LoanProductLookupMapper rm = new LoanProductLookupMapper();
|
||||||
|
|
||||||
String sql = "Select " + rm.restrictedProductsSchema() + " where pm.product_id=? ";
|
String sql = "Select " + rm.restrictedProductsSchema() + " where pm.product_id=? ";
|
||||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
// Check if branch specific products are enabled. If yes, fetch only
|
||||||
String inClause1 = mifosEntityAccessUtil.
|
// products mapped to current user's office
|
||||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
String inClause1 = mifosEntityAccessUtil
|
||||||
MifosEntityType.LOAN_PRODUCT);
|
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(MifosEntityType.LOAN_PRODUCT);
|
||||||
if ( (inClause1 != null) && (!(inClause1.trim().isEmpty())) ) {
|
if ((inClause1 != null) && (!(inClause1.trim().isEmpty()))) {
|
||||||
sql += " and rp.id in ( " + inClause1 + " ) ";
|
sql += " and rp.id in ( " + inClause1 + " ) ";
|
||||||
}
|
}
|
||||||
|
|
||||||
sql += " UNION Select "
|
sql += " UNION Select " + rm.derivedRestrictedProductsSchema() + " where pm.restricted_product_id=?";
|
||||||
+ rm.derivedRestrictedProductsSchema() + " where pm.restricted_product_id=?";
|
|
||||||
|
|
||||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
// Check if branch specific products are enabled. If yes, fetch only
|
||||||
String inClause2 = mifosEntityAccessUtil.
|
// products mapped to current user's office
|
||||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
String inClause2 = mifosEntityAccessUtil
|
||||||
MifosEntityType.LOAN_PRODUCT);
|
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(MifosEntityType.LOAN_PRODUCT);
|
||||||
if ( (inClause2 != null) && (!(inClause2.trim().isEmpty())) ) {
|
if ((inClause2 != null) && (!(inClause2.trim().isEmpty()))) {
|
||||||
sql += " and lp.id in ( " + inClause2 + " ) ";
|
sql += " and lp.id in ( " + inClause2 + " ) ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId });
|
return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId });
|
||||||
}
|
}
|
||||||
@@ -460,16 +463,15 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
|
|
||||||
String sql = "Select " + rm.schema() + " where ";
|
String sql = "Select " + rm.schema() + " where ";
|
||||||
|
|
||||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
// Check if branch specific products are enabled. If yes, fetch only
|
||||||
String inClause = mifosEntityAccessUtil.
|
// products mapped to current user's office
|
||||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
String inClause = mifosEntityAccessUtil
|
||||||
MifosEntityType.LOAN_PRODUCT);
|
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(MifosEntityType.LOAN_PRODUCT);
|
||||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||||
sql += " lp.id in ( " + inClause + " ) and ";
|
sql += " lp.id in ( " + inClause + " ) and ";
|
||||||
}
|
}
|
||||||
|
|
||||||
sql += "lp.id not in ("
|
sql += "lp.id not in (" + "Select pm.restricted_product_id from m_product_mix pm where pm.product_id=? " + "UNION "
|
||||||
+ "Select pm.restricted_product_id from m_product_mix pm where pm.product_id=? " + "UNION "
|
|
||||||
+ "Select pm.product_id from m_product_mix pm where pm.restricted_product_id=?)";
|
+ "Select pm.product_id from m_product_mix pm where pm.restricted_product_id=?)";
|
||||||
|
|
||||||
return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId });
|
return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId });
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
ALTER TABLE `m_product_loan`
|
||||||
|
ADD COLUMN `principal_threshold_for_last_instalment` DECIMAL(5,2) NOT NULL DEFAULT '50';
|
||||||
|
|
||||||
|
update m_product_loan pl set pl.principal_threshold_for_last_instalment = 0 where pl.allow_multiple_disbursals = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user