MIFOSX-1464 : added loan installment round-off
This commit is contained in:
+14
-1
@@ -11032,6 +11032,12 @@ Request Body:
|
|||||||
<tr>
|
<tr>
|
||||||
<td class=fielddesc>Override the default value for rounding currency to multiples of value provided.</td>
|
<td class=fielddesc>Override the default value for rounding currency to multiples of value provided.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>instalmentAmountInMultiplesOf</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>Override the default value for rounding instalment amount to multiples of value provided.</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr class=alt>
|
<tr class=alt>
|
||||||
<td>principal</td>
|
<td>principal</td>
|
||||||
@@ -11133,6 +11139,13 @@ 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>canDefineInstalmentAmount</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class=fielddesc>if provided as true, then fixed instalment amount can be provided from loan account.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -12499,7 +12512,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
|
|||||||
numberOfRepaymentVariationsForBorrowerCycle,
|
numberOfRepaymentVariationsForBorrowerCycle,
|
||||||
interestRateVariationsForBorrowerCycle,
|
interestRateVariationsForBorrowerCycle,
|
||||||
multiDisburseLoan,maxTrancheCount,
|
multiDisburseLoan,maxTrancheCount,
|
||||||
outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds
|
outstandingLoanBalance,overdueDaysForNPA, holdGuaranteeFunds, canDefineInstalmentAmount, instalmentAmountInMultiplesOf
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
+5
@@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
package org.mifosplatform.integrationtests;
|
package org.mifosplatform.integrationtests;
|
||||||
|
|
||||||
import com.jayway.restassured.builder.RequestSpecBuilder;
|
import com.jayway.restassured.builder.RequestSpecBuilder;
|
||||||
|
|||||||
+5
@@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
package org.mifosplatform.integrationtests.common.system;
|
package org.mifosplatform.integrationtests.common.system;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|||||||
+5
@@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
package org.mifosplatform.integrationtests.common.system;
|
package org.mifosplatform.integrationtests.common.system;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|||||||
+17
-11
@@ -74,21 +74,27 @@ public class Money implements Comparable<Money> {
|
|||||||
// round monetary amounts into multiplesof say 20/50.
|
// round monetary amounts into multiplesof say 20/50.
|
||||||
if (inMultiplesOf != null && this.currencyDigitsAfterDecimal == 0 && inMultiplesOf > 0 && amountScaled.doubleValue() > 0) {
|
if (inMultiplesOf != null && this.currencyDigitsAfterDecimal == 0 && inMultiplesOf > 0 && amountScaled.doubleValue() > 0) {
|
||||||
final double existingVal = amountScaled.doubleValue();
|
final double existingVal = amountScaled.doubleValue();
|
||||||
final double ceilingOfValue = ceiling(existingVal, inMultiplesOf);
|
amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, inMultiplesOf));
|
||||||
final double floorOfValue = floor(existingVal, inMultiplesOf);
|
|
||||||
|
|
||||||
final double floorDiff = existingVal - floorOfValue;
|
|
||||||
final double ceilDiff = ceilingOfValue - existingVal;
|
|
||||||
|
|
||||||
if (ceilDiff > floorDiff) {
|
|
||||||
amountScaled = BigDecimal.valueOf(floorOfValue);
|
|
||||||
} else {
|
|
||||||
amountScaled = BigDecimal.valueOf(ceilingOfValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN);
|
this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double roundToMultiplesOf(final double existingVal, final Integer inMultiplesOf) {
|
||||||
|
double amountScaled = existingVal;
|
||||||
|
final double ceilingOfValue = ceiling(existingVal, inMultiplesOf);
|
||||||
|
final double floorOfValue = floor(existingVal, inMultiplesOf);
|
||||||
|
|
||||||
|
final double floorDiff = existingVal - floorOfValue;
|
||||||
|
final double ceilDiff = ceilingOfValue - existingVal;
|
||||||
|
|
||||||
|
if (ceilDiff > floorDiff) {
|
||||||
|
amountScaled = floorOfValue;
|
||||||
|
} else {
|
||||||
|
amountScaled = ceilingOfValue;
|
||||||
|
}
|
||||||
|
return amountScaled;
|
||||||
|
}
|
||||||
|
|
||||||
public static double ceiling(final double n, final double s) {
|
public static double ceiling(final double n, final double s) {
|
||||||
double c;
|
double c;
|
||||||
|
|
||||||
|
|||||||
+79
-56
@@ -152,6 +152,8 @@ public class LoanAccountData {
|
|||||||
|
|
||||||
private final Boolean multiDisburseLoan;
|
private final Boolean multiDisburseLoan;
|
||||||
|
|
||||||
|
private final Boolean canDefineInstalmentAmount;
|
||||||
|
|
||||||
private final BigDecimal fixedEmiAmount;
|
private final BigDecimal fixedEmiAmount;
|
||||||
|
|
||||||
private final BigDecimal maxOutstandingLoanBalance;
|
private final BigDecimal maxOutstandingLoanBalance;
|
||||||
@@ -260,6 +262,7 @@ public class LoanAccountData {
|
|||||||
final PortfolioAccountData linkedAccount = null;
|
final PortfolioAccountData linkedAccount = null;
|
||||||
final Collection<DisbursementData> disbursementData = null;
|
final Collection<DisbursementData> disbursementData = null;
|
||||||
final Boolean multiDisburseLoan = null;
|
final Boolean multiDisburseLoan = null;
|
||||||
|
final Boolean canDefineInstalmentAmount = null;
|
||||||
final BigDecimal fixedEmiAmount = null;
|
final BigDecimal fixedEmiAmount = null;
|
||||||
final BigDecimal maxOutstandingLoanBalance = null;
|
final BigDecimal maxOutstandingLoanBalance = null;
|
||||||
final Collection<LoanTermVariationsData> emiAmountVariations = null;
|
final Collection<LoanTermVariationsData> emiAmountVariations = null;
|
||||||
@@ -289,9 +292,10 @@ public class LoanAccountData {
|
|||||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||||
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
|
disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
|
||||||
product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
|
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||||
isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
|
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||||
|
createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -377,6 +381,7 @@ public class LoanAccountData {
|
|||||||
final PortfolioAccountData linkedAccount = null;
|
final PortfolioAccountData linkedAccount = null;
|
||||||
final Collection<DisbursementData> disbursementData = null;
|
final Collection<DisbursementData> disbursementData = null;
|
||||||
final Boolean multiDisburseLoan = null;
|
final Boolean multiDisburseLoan = null;
|
||||||
|
final Boolean canDefineInstalmentAmount = null;
|
||||||
final BigDecimal fixedEmiAmount = null;
|
final BigDecimal fixedEmiAmount = null;
|
||||||
final BigDecimal maxOutstandingLoanBalance = null;
|
final BigDecimal maxOutstandingLoanBalance = null;
|
||||||
final Collection<LoanTermVariationsData> emiAmountVariations = null;
|
final Collection<LoanTermVariationsData> emiAmountVariations = null;
|
||||||
@@ -406,9 +411,10 @@ public class LoanAccountData {
|
|||||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||||
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
|
disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
|
||||||
product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
|
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||||
isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
|
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||||
|
createStandingInstructionAtDisbursement);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,10 +436,11 @@ public class LoanAccountData {
|
|||||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
|
||||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
|
acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -520,6 +527,7 @@ public class LoanAccountData {
|
|||||||
final PortfolioAccountData linkedAccount = null;
|
final PortfolioAccountData linkedAccount = null;
|
||||||
final Collection<DisbursementData> disbursementData = null;
|
final Collection<DisbursementData> disbursementData = null;
|
||||||
final Boolean multiDisburseLoan = null;
|
final Boolean multiDisburseLoan = null;
|
||||||
|
final Boolean canDefineInstalmentAmount = null;
|
||||||
final BigDecimal fixedEmiAmount = null;
|
final BigDecimal fixedEmiAmount = null;
|
||||||
final BigDecimal maxOutstandingBalance = null;
|
final BigDecimal maxOutstandingBalance = null;
|
||||||
final Collection<LoanTermVariationsData> emiAmountVariations = null;
|
final Collection<LoanTermVariationsData> emiAmountVariations = null;
|
||||||
@@ -549,9 +557,9 @@ public class LoanAccountData {
|
|||||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||||
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance, emiAmountVariations, memberVariations, product,
|
disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmount, maxOutstandingBalance, emiAmountVariations,
|
||||||
inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
|
||||||
interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
|
isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
|
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
|
||||||
@@ -572,10 +580,11 @@ public class LoanAccountData {
|
|||||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
|
||||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
|
acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
|
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
|
||||||
@@ -707,10 +716,11 @@ public class LoanAccountData {
|
|||||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
|
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
|
||||||
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||||
disbursementData, product.getMultiDisburseLoan(), fixedEmi, product.getOutstandingLoanBalance(), emiAmountVariations,
|
disbursementData, product.getMultiDisburseLoan(), product.canDefineInstalmentAmount(), fixedEmi,
|
||||||
memberVariations, product, inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA,
|
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
|
||||||
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
|
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(),
|
||||||
product.toLoanInterestRecalculationData(), originalSchedule, createStandingInstructionAtDisbursement);
|
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
||||||
|
originalSchedule, createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||||
@@ -760,11 +770,11 @@ public class LoanAccountData {
|
|||||||
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
||||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
|
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
|
||||||
loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,
|
loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,
|
||||||
acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount,
|
acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(),
|
||||||
product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears,
|
product.canDefineInstalmentAmount(), acc.fixedEmiAmount, product.getOutstandingLoanBalance(), acc.emiAmountVariations,
|
||||||
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(),
|
acc.memberVariations, product, acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA,
|
||||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
|
||||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
|
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -786,9 +796,9 @@ public class LoanAccountData {
|
|||||||
final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline,
|
final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline,
|
||||||
final LoanSummaryData loanSummary, final BigDecimal feeChargesDueAtDisbursementCharged,
|
final LoanSummaryData loanSummary, final BigDecimal feeChargesDueAtDisbursementCharged,
|
||||||
final Boolean syncDisbursementWithMeeting, final Integer loancounter, final Integer loanProductCounter,
|
final Boolean syncDisbursementWithMeeting, final Integer loancounter, final Integer loanProductCounter,
|
||||||
final Boolean multiDisburseLoan, final BigDecimal fixedEmiAmont, final BigDecimal outstandingLoanBalance,
|
final Boolean multiDisburseLoan, Boolean canDefineInstalmentAmount, final BigDecimal fixedEmiAmont,
|
||||||
final Boolean inArrears, final Integer graceOnArrearsAgeing, final Boolean isNPA, final EnumOptionData daysInMonthType,
|
final BigDecimal outstandingLoanBalance, final Boolean inArrears, final Integer graceOnArrearsAgeing, final Boolean isNPA,
|
||||||
final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||||
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement) {
|
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement) {
|
||||||
|
|
||||||
final LoanScheduleData repaymentSchedule = null;
|
final LoanScheduleData repaymentSchedule = null;
|
||||||
@@ -837,9 +847,9 @@ public class LoanAccountData {
|
|||||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
|
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
|
||||||
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||||
disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance, emiAmountVariations, memberVariations, product,
|
disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmont, outstandingLoanBalance, emiAmountVariations,
|
||||||
inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
|
||||||
interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
|
isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -878,9 +888,10 @@ public class LoanAccountData {
|
|||||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||||
disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, emiAmountVariations,
|
disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
|
||||||
acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType,
|
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||||
acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||||
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement);
|
acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -913,10 +924,11 @@ public class LoanAccountData {
|
|||||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
|
||||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
|
acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||||
@@ -973,10 +985,11 @@ public class LoanAccountData {
|
|||||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
|
||||||
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears,
|
||||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
|
acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
||||||
@@ -999,10 +1012,11 @@ public class LoanAccountData {
|
|||||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
|
||||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData,
|
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
|
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
|
||||||
|
acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||||
@@ -1023,10 +1037,11 @@ public class LoanAccountData {
|
|||||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||||
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
|
||||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||||
originalSchedule, acc.createStandingInstructionAtDisbursement);
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
|
||||||
|
acc.createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanAccountData(
|
private LoanAccountData(
|
||||||
@@ -1081,11 +1096,11 @@ public class LoanAccountData {
|
|||||||
final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter,
|
final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter,
|
||||||
final Collection<NoteData> notes, final Collection<PortfolioAccountData> accountLinkingOptions,
|
final Collection<NoteData> notes, final Collection<PortfolioAccountData> accountLinkingOptions,
|
||||||
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
|
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
|
||||||
final Boolean multiDisburseLoan, BigDecimal fixedEmiAmount, final BigDecimal maxOutstandingLoanBalance,
|
final Boolean multiDisburseLoan, final Boolean canDefineInstalmentAmount, BigDecimal fixedEmiAmount,
|
||||||
final Collection<LoanTermVariationsData> emiAmountVariations, final Map<Long, LoanBorrowerCycleData> memberVariations,
|
final BigDecimal maxOutstandingLoanBalance, final Collection<LoanTermVariationsData> emiAmountVariations,
|
||||||
final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing,
|
final Map<Long, LoanBorrowerCycleData> memberVariations, final LoanProductData product, final Boolean inArrears,
|
||||||
final Collection<ChargeData> overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType,
|
final Integer graceOnArrearsAgeing, final Collection<ChargeData> overdueCharges, final Boolean isNPA,
|
||||||
final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||||
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
|
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
|
||||||
final Boolean createStandingInstructionAtDisbursement) {
|
final Boolean createStandingInstructionAtDisbursement) {
|
||||||
|
|
||||||
@@ -1209,6 +1224,14 @@ public class LoanAccountData {
|
|||||||
this.accountLinkingOptions = accountLinkingOptions;
|
this.accountLinkingOptions = accountLinkingOptions;
|
||||||
this.disbursementDetails = disbursementDetails;
|
this.disbursementDetails = disbursementDetails;
|
||||||
this.multiDisburseLoan = multiDisburseLoan;
|
this.multiDisburseLoan = multiDisburseLoan;
|
||||||
|
|
||||||
|
Boolean canDefineEMIAmount = canDefineInstalmentAmount;
|
||||||
|
if (canDefineInstalmentAmount == null) {
|
||||||
|
canDefineEMIAmount = multiDisburseLoan;
|
||||||
|
} else if (multiDisburseLoan != null) {
|
||||||
|
canDefineEMIAmount = canDefineInstalmentAmount || multiDisburseLoan;
|
||||||
|
}
|
||||||
|
this.canDefineInstalmentAmount = canDefineEMIAmount;
|
||||||
this.fixedEmiAmount = fixedEmiAmount;
|
this.fixedEmiAmount = fixedEmiAmount;
|
||||||
this.maxOutstandingLoanBalance = maxOutstandingLoanBalance;
|
this.maxOutstandingLoanBalance = maxOutstandingLoanBalance;
|
||||||
|
|
||||||
|
|||||||
+31
-16
@@ -1195,9 +1195,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
if (command.isChangeInBooleanParameterNamed(createSiAtDisbursementParameterName, shouldCreateStandingInstructionAtDisbursement())) {
|
if (command.isChangeInBooleanParameterNamed(createSiAtDisbursementParameterName, shouldCreateStandingInstructionAtDisbursement())) {
|
||||||
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(createSiAtDisbursementParameterName);
|
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(createSiAtDisbursementParameterName);
|
||||||
actualChanges.put(createSiAtDisbursementParameterName, valueAsInput);
|
actualChanges.put(createSiAtDisbursementParameterName, valueAsInput);
|
||||||
this.createStandingInstructionAtDisbursement = valueAsInput;
|
this.createStandingInstructionAtDisbursement = valueAsInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String externalIdParamName = "externalId";
|
final String externalIdParamName = "externalId";
|
||||||
if (command.isChangeInStringParameterNamed(externalIdParamName, this.externalId)) {
|
if (command.isChangeInStringParameterNamed(externalIdParamName, this.externalId)) {
|
||||||
final String newValue = command.stringValueOfParameterNamed(externalIdParamName);
|
final String newValue = command.stringValueOfParameterNamed(externalIdParamName);
|
||||||
@@ -1398,9 +1398,6 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
if (loanProduct.isMultiDisburseLoan()) {
|
if (loanProduct.isMultiDisburseLoan()) {
|
||||||
updateDisbursementDetails(command, actualChanges);
|
updateDisbursementDetails(command, actualChanges);
|
||||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) {
|
|
||||||
this.fixedEmiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
|
||||||
}
|
|
||||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||||
this.maxOutstandingLoanBalance)) {
|
this.maxOutstandingLoanBalance)) {
|
||||||
this.maxOutstandingLoanBalance = command
|
this.maxOutstandingLoanBalance = command
|
||||||
@@ -1417,6 +1414,15 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.disbursementDetails.clear();
|
this.disbursementDetails.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loanProduct.isMultiDisburseLoan() || loanProduct.canDefineInstalmentAmount()) {
|
||||||
|
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) {
|
||||||
|
this.fixedEmiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||||
|
actualChanges.put(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount);
|
||||||
|
actualChanges.put("recalculateLoanSchedule", true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
this.fixedEmiAmount = null;
|
this.fixedEmiAmount = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2076,10 +2082,15 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
|
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
|
||||||
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser) {
|
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser) {
|
||||||
boolean isEmiAmountChanged = false;
|
boolean isEmiAmountChanged = false;
|
||||||
if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstalmentAmount()) && emiAmount != null
|
||||||
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
&& emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
||||||
actualDisbursementDate.toDate(), emiAmount, this);
|
if (this.loanProduct.isMultiDisburseLoan()) {
|
||||||
this.loanTermVariations.add(loanVariationTerms);
|
LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
|
||||||
|
actualDisbursementDate.toDate(), emiAmount, this);
|
||||||
|
this.loanTermVariations.add(loanVariationTerms);
|
||||||
|
} else {
|
||||||
|
this.fixedEmiAmount = emiAmount;
|
||||||
|
}
|
||||||
isEmiAmountChanged = true;
|
isEmiAmountChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2315,7 +2326,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.getInstalmentAmountInMultiplesOf());
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||||
@@ -4639,7 +4651,7 @@ 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.getInstalmentAmountInMultiplesOf());
|
||||||
return loanApplicationTerms;
|
return loanApplicationTerms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4682,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.getInstalmentAmountInMultiplesOf());
|
||||||
|
|
||||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
||||||
LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc, charges());
|
LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc, charges());
|
||||||
@@ -4880,10 +4893,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
|
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
|
||||||
loanVariationTermsData.add(data);
|
loanVariationTermsData.add(data);
|
||||||
|
|
||||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
|
return LoanApplicationTerms
|
||||||
dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType,
|
||||||
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData,
|
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||||
maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate);
|
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount,
|
||||||
|
disbursementData, maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate,
|
||||||
|
this.loanProduct.getInstalmentAmountInMultiplesOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+9
-20
@@ -117,7 +117,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
|
outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
|
||||||
}
|
}
|
||||||
Money reducePrincipal = totalCumulativePrincipal.zero();
|
Money reducePrincipal = totalCumulativePrincipal.zero();
|
||||||
Money fixedEmiAmount = totalCumulativePrincipal.zero();
|
|
||||||
int daysCalcForInstallmentNumber = 0;
|
int daysCalcForInstallmentNumber = 0;
|
||||||
LocalDate scheduleStartDateAsPerFrequency = periodStartDate;
|
LocalDate scheduleStartDateAsPerFrequency = periodStartDate;
|
||||||
while (!outstandingBalance.isZero()) {
|
while (!outstandingBalance.isZero()) {
|
||||||
@@ -191,7 +190,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
BigDecimal disburseAmt = BigDecimal.ZERO;
|
BigDecimal disburseAmt = BigDecimal.ZERO;
|
||||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||||
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
||||||
fixedEmiAmount = fixedEmiAmount.zero().plus(loanApplicationTerms.getFixedEmiAmount());
|
|
||||||
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
||||||
LocalDate tillDate = scheduledDueDate;
|
LocalDate tillDate = scheduledDueDate;
|
||||||
if (prepayDate != null && prepayDate.isBefore(tillDate)) {
|
if (prepayDate != null && prepayDate.isBefore(tillDate)) {
|
||||||
@@ -233,16 +231,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
if (!loanApplicationTerms.isMultiDisburseLoan()) {
|
if (!loanApplicationTerms.isMultiDisburseLoan()) {
|
||||||
loanApplicationTerms.setFixedEmiAmount(null);
|
loanApplicationTerms.setFixedEmiAmount(null);
|
||||||
}
|
}
|
||||||
fixedEmiAmount = fixedEmiAmount.zero();
|
|
||||||
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
||||||
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
|
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
|
||||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
||||||
reducePrincipal = reducePrincipal.zero();
|
reducePrincipal = reducePrincipal.zero();
|
||||||
break;
|
break;
|
||||||
case REDUCE_NUMBER_OF_INSTALLMENTS:
|
case REDUCE_NUMBER_OF_INSTALLMENTS:
|
||||||
if (fixedEmiAmount.isGreaterThanZero()) {
|
|
||||||
loanApplicationTerms.setFixedEmiAmount(fixedEmiAmount.getAmount());
|
|
||||||
}
|
|
||||||
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
outstandingBalance = outstandingBalance.minus(reducePrincipal);
|
||||||
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
|
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
|
||||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
|
||||||
@@ -385,14 +379,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// Exclude principal portion for interest only installments
|
// Exclude principal portion for interest only installments
|
||||||
if (!recalculatedInterestComponent) {
|
if (!recalculatedInterestComponent) {
|
||||||
principalForThisPeriod = principalInterestForThisPeriod.principal();
|
principalForThisPeriod = principalInterestForThisPeriod.principal();
|
||||||
if (diffAmt != null && !diffAmt.isEmpty() && loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
|
if (((diffAmt != null && !diffAmt.isEmpty()) || interestToBeAdded.compareTo(BigDecimal.ZERO) == 1)
|
||||||
Money principalToBeAdjust = principalForThisPeriod.zero();
|
&& loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
|
||||||
if (fixedEmiAmount.isZero()) {
|
Money principalToBeAdjust = Money.of(currency, loanApplicationTerms.getFixedEmiAmount()).minus(principalForThisPeriod)
|
||||||
principalToBeAdjust = principalInterestForThisPeriod.interest().minus(interestForThisinstallment);
|
.minus(interestForThisinstallment);
|
||||||
|
|
||||||
} else {
|
|
||||||
principalToBeAdjust = fixedEmiAmount.minus(principalForThisPeriod).minus(interestForThisinstallment);
|
|
||||||
}
|
|
||||||
principalForThisPeriod = principalForThisPeriod.plus(principalToBeAdjust);
|
principalForThisPeriod = principalForThisPeriod.plus(principalToBeAdjust);
|
||||||
principalForThisPeriod = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisPeriod,
|
principalForThisPeriod = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisPeriod,
|
||||||
totalCumulativePrincipal.plus(principalForThisPeriod), periodNumber);
|
totalCumulativePrincipal.plus(principalForThisPeriod), periodNumber);
|
||||||
@@ -407,9 +397,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixedEmiAmount.isZero() && !recalculatedInterestComponent) {
|
|
||||||
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
|
|
||||||
}
|
|
||||||
reducePrincipal = reducePrincipal.minus(reducePrincipalForCurrentInstallment);
|
reducePrincipal = reducePrincipal.minus(reducePrincipalForCurrentInstallment);
|
||||||
|
|
||||||
if (principalForThisPeriod.isGreaterThan(reducePrincipalForCurrentInstallment)) {
|
if (principalForThisPeriod.isGreaterThan(reducePrincipalForCurrentInstallment)) {
|
||||||
@@ -543,6 +530,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loanApplicationTerms.resetFixedEmiAmount();
|
||||||
|
|
||||||
return LoanScheduleModel.from(periods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected,
|
return LoanScheduleModel.from(periods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected,
|
||||||
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected,
|
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected,
|
||||||
totalOutstanding);
|
totalOutstanding);
|
||||||
@@ -794,12 +783,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
Money principalDue = Money.of(currency, period.principalDue());
|
Money principalDue = Money.of(currency, period.principalDue());
|
||||||
Money interestDue = Money.of(currency, period.interestDue());
|
Money interestDue = Money.of(currency, period.interestDue());
|
||||||
|
|
||||||
if(principalDue.isZero() && interestDue.isZero()) {
|
if (principalDue.isZero() && interestDue.isZero()) {
|
||||||
period.updateFeeChargesDue(Money.zero(currency));
|
period.updateFeeChargesDue(Money.zero(currency));
|
||||||
period.updatePenaltyChargesDue(Money.zero(currency));
|
period.updatePenaltyChargesDue(Money.zero(currency));
|
||||||
}
|
}
|
||||||
|
|
||||||
Money feeChargesDue = Money.of(currency, period.feeChargesDue());
|
Money feeChargesDue = Money.of(currency, period.feeChargesDue());
|
||||||
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
|
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
|
||||||
|
|
||||||
|
|||||||
+35
-23
@@ -112,6 +112,8 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
private BigDecimal fixedEmiAmount;
|
private BigDecimal fixedEmiAmount;
|
||||||
|
|
||||||
|
private BigDecimal actualFixedEmiAmount;
|
||||||
|
|
||||||
private final BigDecimal maxOutstandingBalance;
|
private final BigDecimal maxOutstandingBalance;
|
||||||
|
|
||||||
private final List<LoanTermVariationsData> emiAmountVariations;
|
private final List<LoanTermVariationsData> emiAmountVariations;
|
||||||
@@ -132,6 +134,8 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
private final RecalculationFrequencyType recalculationFrequencyType;
|
private final RecalculationFrequencyType recalculationFrequencyType;
|
||||||
|
|
||||||
|
private final Integer instalmentAmountInMultiplesOf;
|
||||||
|
|
||||||
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 +148,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,
|
||||||
|
Integer instalmentAmountInMultiplesOf) {
|
||||||
|
|
||||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
||||||
@@ -157,7 +162,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, instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||||
@@ -166,7 +171,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,
|
||||||
|
Integer instalmentAmountInMultiplesOf) {
|
||||||
|
|
||||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||||
@@ -201,7 +207,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,
|
||||||
|
instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||||
@@ -211,7 +218,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, Integer instalmentAmountInMultiplesOf) {
|
||||||
|
|
||||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||||
@@ -248,7 +255,7 @@ 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, instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||||
@@ -266,7 +273,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.instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
@@ -283,7 +290,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 Integer instalmentAmountInMultiplesOf) {
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.loanTermFrequency = loanTermFrequency;
|
this.loanTermFrequency = loanTermFrequency;
|
||||||
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
||||||
@@ -313,6 +321,7 @@ public final class LoanApplicationTerms {
|
|||||||
this.inArrearsTolerance = inArrearsTolerance;
|
this.inArrearsTolerance = inArrearsTolerance;
|
||||||
this.multiDisburseLoan = multiDisburseLoan;
|
this.multiDisburseLoan = multiDisburseLoan;
|
||||||
this.fixedEmiAmount = emiAmount;
|
this.fixedEmiAmount = emiAmount;
|
||||||
|
this.actualFixedEmiAmount = emiAmount;
|
||||||
this.disbursementDatas = disbursementDatas;
|
this.disbursementDatas = disbursementDatas;
|
||||||
this.maxOutstandingBalance = maxOutstandingBalance;
|
this.maxOutstandingBalance = maxOutstandingBalance;
|
||||||
this.emiAmountVariations = emiAmountVariations;
|
this.emiAmountVariations = emiAmountVariations;
|
||||||
@@ -324,6 +333,7 @@ public final class LoanApplicationTerms {
|
|||||||
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
|
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
|
||||||
this.restCalendarInstance = restCalendarInstance;
|
this.restCalendarInstance = restCalendarInstance;
|
||||||
this.recalculationFrequencyType = recalculationFrequencyType;
|
this.recalculationFrequencyType = recalculationFrequencyType;
|
||||||
|
this.instalmentAmountInMultiplesOf = instalmentAmountInMultiplesOf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||||
@@ -508,15 +518,6 @@ public final class LoanApplicationTerms {
|
|||||||
return periodNumber == numberOfRepayments;
|
return periodNumber == numberOfRepayments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money calculateTotalDueForEqualInstallmentLoan(final BigDecimal periodicInterestRate) {
|
|
||||||
|
|
||||||
final int periodsElapsed = 0;
|
|
||||||
final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
|
|
||||||
double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
|
|
||||||
|
|
||||||
return Money.of(this.principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* general method to calculate totalInterestDue discounting any grace
|
* general method to calculate totalInterestDue discounting any grace
|
||||||
* settings
|
* settings
|
||||||
@@ -801,14 +802,21 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
private double paymentPerPeriod(final BigDecimal periodicInterestRate, final Money balance, final int periodsElapsed) {
|
private double paymentPerPeriod(final BigDecimal periodicInterestRate, final Money balance, final int periodsElapsed) {
|
||||||
|
|
||||||
if (fixedEmiAmount != null) { return fixedEmiAmount.doubleValue(); }
|
if (this.fixedEmiAmount == null) {
|
||||||
final double futureValue = 0;
|
final double futureValue = 0;
|
||||||
final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
|
final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
|
||||||
|
|
||||||
final Integer periodsRemaining = this.numberOfRepayments - periodsElapsed;
|
final Integer periodsRemaining = this.numberOfRepayments - periodsElapsed;
|
||||||
|
|
||||||
return FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(), principalDouble, futureValue,
|
double installmentAmount = FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(),
|
||||||
false);
|
principalDouble, futureValue, false);
|
||||||
|
|
||||||
|
if (this.instalmentAmountInMultiplesOf != null) {
|
||||||
|
installmentAmount = Money.roundToMultiplesOf(installmentAmount, this.instalmentAmountInMultiplesOf);
|
||||||
|
}
|
||||||
|
setFixedEmiAmount(BigDecimal.valueOf(installmentAmount));
|
||||||
|
}
|
||||||
|
return this.fixedEmiAmount.doubleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
|
private Money calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
|
||||||
@@ -1014,6 +1022,10 @@ public final class LoanApplicationTerms {
|
|||||||
this.fixedEmiAmount = fixedEmiAmount;
|
this.fixedEmiAmount = fixedEmiAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetFixedEmiAmount() {
|
||||||
|
this.fixedEmiAmount = this.actualFixedEmiAmount;
|
||||||
|
}
|
||||||
|
|
||||||
public LoanRescheduleStrategyMethod getLoanRescheduleStrategyMethod() {
|
public LoanRescheduleStrategyMethod getLoanRescheduleStrategyMethod() {
|
||||||
return LoanRescheduleStrategyMethod.REDUCE_EMI_AMOUNT;
|
return LoanRescheduleStrategyMethod.REDUCE_EMI_AMOUNT;
|
||||||
}
|
}
|
||||||
|
|||||||
+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 Integer instalmentAmountInMultiplesOf = loanProduct.getInstalmentAmountInMultiplesOf();
|
||||||
|
|
||||||
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, instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DisbursementData> fetchDisbursementData(final JsonObject command) {
|
private List<DisbursementData> fetchDisbursementData(final JsonObject command) {
|
||||||
|
|||||||
+5
-5
@@ -95,19 +95,19 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
* TODO: Vishwas, this is probably not required, test and remove the
|
* TODO: Vishwas, this is probably not required, test and remove the
|
||||||
* same
|
* same
|
||||||
**/
|
**/
|
||||||
|
final Long productId = this.fromJsonHelper.extractLongNamed("productId", query.parsedJson());
|
||||||
|
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||||
|
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||||
|
|
||||||
if (validateParams) {
|
if (validateParams) {
|
||||||
boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans();
|
boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans();
|
||||||
this.loanApiJsonDeserializer.validateForCreate(query.json(), isMeetingMandatoryForJLGLoans);
|
this.loanApiJsonDeserializer.validateForCreate(query.json(), isMeetingMandatoryForJLGLoans, loanProduct);
|
||||||
}
|
}
|
||||||
this.fromApiJsonDeserializer.validate(query.json());
|
this.fromApiJsonDeserializer.validate(query.json());
|
||||||
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||||
|
|
||||||
final Long productId = this.fromJsonHelper.extractLongNamed("productId", query.parsedJson());
|
|
||||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
|
||||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
|
||||||
|
|
||||||
if (loanProduct.useBorrowerCycle()) {
|
if (loanProduct.useBorrowerCycle()) {
|
||||||
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", query.parsedJson());
|
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", query.parsedJson());
|
||||||
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", query.parsedJson());
|
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", query.parsedJson());
|
||||||
|
|||||||
+13
-2
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
|||||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
@@ -78,7 +79,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
this.apiJsonHelper = apiJsonHelper;
|
this.apiJsonHelper = apiJsonHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans) {
|
public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans, final LoanProduct loanProduct) {
|
||||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
|
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
@@ -362,6 +363,11 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
|
||||||
|
if (!(loanProduct.canDefineInstalmentAmount() || loanProduct.isMultiDisburseLoan())) {
|
||||||
|
List<String> unsupportedParameterList = new ArrayList<>();
|
||||||
|
unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
|
||||||
|
throw new UnsupportedParameterException(unsupportedParameterList);
|
||||||
|
}
|
||||||
final BigDecimal emiAnount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
final BigDecimal emiAnount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
||||||
element);
|
element);
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.emiAmountParameterName).value(emiAnount).ignoreIfNull().positiveAmount();
|
baseDataValidator.reset().parameter(LoanApiConstants.emiAmountParameterName).value(emiAnount).ignoreIfNull().positiveAmount();
|
||||||
@@ -377,7 +383,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateForModify(final String json) {
|
public void validateForModify(final String json, final LoanProduct loanProduct) {
|
||||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
|
|
||||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
@@ -705,6 +711,11 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.emiAmountParameterName, element)) {
|
||||||
|
if (!(loanProduct.canDefineInstalmentAmount() || loanProduct.isMultiDisburseLoan())) {
|
||||||
|
List<String> unsupportedParameterList = new ArrayList<>();
|
||||||
|
unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
|
||||||
|
throw new UnsupportedParameterException(unsupportedParameterList);
|
||||||
|
}
|
||||||
final BigDecimal emiAnount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
final BigDecimal emiAnount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName,
|
||||||
element);
|
element);
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.emiAmountParameterName).value(emiAnount).ignoreIfNull().positiveAmount();
|
baseDataValidator.reset().parameter(LoanApiConstants.emiAmountParameterName).value(emiAnount).ignoreIfNull().positiveAmount();
|
||||||
|
|||||||
+6
-7
@@ -193,15 +193,15 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
try {
|
try {
|
||||||
final AppUser currentUser = getAppUserIfPresent();
|
final AppUser currentUser = getAppUserIfPresent();
|
||||||
boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans();
|
boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans();
|
||||||
|
final Long productId = this.fromJsonHelper.extractLongNamed("productId", command.parsedJson());
|
||||||
|
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||||
|
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||||
|
|
||||||
this.fromApiJsonDeserializer.validateForCreate(command.json(), isMeetingMandatoryForJLGLoans);
|
this.fromApiJsonDeserializer.validateForCreate(command.json(), isMeetingMandatoryForJLGLoans, loanProduct);
|
||||||
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||||
|
|
||||||
final Long productId = this.fromJsonHelper.extractLongNamed("productId", command.parsedJson());
|
|
||||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
|
||||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
|
||||||
if (loanProduct.useBorrowerCycle()) {
|
if (loanProduct.useBorrowerCycle()) {
|
||||||
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", command.parsedJson());
|
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", command.parsedJson());
|
||||||
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", command.parsedJson());
|
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", command.parsedJson());
|
||||||
@@ -376,10 +376,9 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
AppUser currentUser = getAppUserIfPresent();
|
AppUser currentUser = getAppUserIfPresent();
|
||||||
|
|
||||||
this.fromApiJsonDeserializer.validateForModify(command.json());
|
|
||||||
|
|
||||||
final Loan existingLoanApplication = retrieveLoanBy(loanId);
|
final Loan existingLoanApplication = retrieveLoanBy(loanId);
|
||||||
|
this.fromApiJsonDeserializer.validateForModify(command.json(), existingLoanApplication.loanProduct());
|
||||||
|
|
||||||
checkClientOrGroupActive(existingLoanApplication);
|
checkClientOrGroupActive(existingLoanApplication);
|
||||||
|
|
||||||
if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(
|
if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(
|
||||||
|
|||||||
+11
-7
@@ -159,8 +159,9 @@ public class LoanAssembler {
|
|||||||
final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
|
final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
|
||||||
final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
|
final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
|
||||||
final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
|
final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
|
||||||
final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper.extractBooleanNamed("createStandingInstructionAtDisbursement", element);
|
final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper.extractBooleanNamed(
|
||||||
|
"createStandingInstructionAtDisbursement", element);
|
||||||
|
|
||||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||||
|
|
||||||
@@ -198,11 +199,13 @@ public class LoanAssembler {
|
|||||||
final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeStr);
|
final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeStr);
|
||||||
Set<LoanDisbursementDetails> disbursementDetails = null;
|
Set<LoanDisbursementDetails> disbursementDetails = null;
|
||||||
BigDecimal fixedEmiAmount = null;
|
BigDecimal fixedEmiAmount = null;
|
||||||
|
if (loanProduct.isMultiDisburseLoan() || loanProduct.canDefineInstalmentAmount()) {
|
||||||
|
fixedEmiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element);
|
||||||
|
}
|
||||||
BigDecimal maxOutstandingLoanBalance = null;
|
BigDecimal maxOutstandingLoanBalance = null;
|
||||||
if (loanProduct.isMultiDisburseLoan()) {
|
if (loanProduct.isMultiDisburseLoan()) {
|
||||||
disbursementDetails = fetchDisbursementData(element.getAsJsonObject());
|
disbursementDetails = fetchDisbursementData(element.getAsJsonObject());
|
||||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
|
||||||
fixedEmiAmount = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.emiAmountParameterName, element, locale);
|
|
||||||
maxOutstandingLoanBalance = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
maxOutstandingLoanBalance = this.fromApiJsonHelper.extractBigDecimalNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||||
element, locale);
|
element, locale);
|
||||||
if (disbursementDetails.isEmpty()) {
|
if (disbursementDetails.isEmpty()) {
|
||||||
@@ -234,14 +237,14 @@ public class LoanAssembler {
|
|||||||
|
|
||||||
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(),
|
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(),
|
||||||
loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges,
|
loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges,
|
||||||
collateral, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
collateral, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||||
createStandingInstructionAtDisbursement);
|
createStandingInstructionAtDisbursement);
|
||||||
|
|
||||||
} else if (group != null) {
|
} else if (group != null) {
|
||||||
|
|
||||||
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
|
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
|
||||||
loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
|
loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
|
||||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||||
createStandingInstructionAtDisbursement);
|
createStandingInstructionAtDisbursement);
|
||||||
|
|
||||||
} else if (client != null) {
|
} else if (client != null) {
|
||||||
@@ -264,8 +267,9 @@ public class LoanAssembler {
|
|||||||
loanDisbursementDetails.updateLoan(loanApplication);
|
loanDisbursementDetails.updateLoan(loanApplication);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final LocalDate recalculationRestFrequencyDate = this.fromApiJsonHelper.extractLocalDateNamed(LoanProductConstants.recalculationRestFrequencyDateParamName, element);
|
final LocalDate recalculationRestFrequencyDate = this.fromApiJsonHelper.extractLocalDateNamed(
|
||||||
|
LoanProductConstants.recalculationRestFrequencyDateParamName, element);
|
||||||
|
|
||||||
final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element);
|
final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element);
|
||||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
|||||||
+5
-3
@@ -498,6 +498,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
+ " l.loan_type_enum as loanType, l.loanpurpose_cv_id as loanPurposeId, cv.code_value as loanPurposeName,"
|
+ " l.loan_type_enum as loanType, l.loanpurpose_cv_id as loanPurposeId, cv.code_value as loanPurposeName,"
|
||||||
+ " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription,"
|
+ " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription,"
|
||||||
+ " lp.allow_multiple_disbursals as multiDisburseLoan,"
|
+ " lp.allow_multiple_disbursals as multiDisburseLoan,"
|
||||||
|
+ " lp.can_define_fixed_emi_amount as canDefineInstalmentAmount,"
|
||||||
+ " c.id as clientId, c.display_name as clientName, c.office_id as clientOfficeId,"
|
+ " c.id as clientId, c.display_name as clientName, c.office_id as clientOfficeId,"
|
||||||
+ " g.id as groupId, g.display_name as groupName,"
|
+ " g.id as groupId, g.display_name as groupName,"
|
||||||
+ " g.office_id as groupOfficeId, g.staff_id As groupStaffId , g.parent_id as groupParentId, "
|
+ " g.office_id as groupOfficeId, g.staff_id As groupStaffId , g.parent_id as groupParentId, "
|
||||||
@@ -627,6 +628,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
final String loanProductName = rs.getString("loanProductName");
|
final String loanProductName = rs.getString("loanProductName");
|
||||||
final String loanProductDescription = rs.getString("loanProductDescription");
|
final String loanProductDescription = rs.getString("loanProductDescription");
|
||||||
final Boolean multiDisburseLoan = rs.getBoolean("multiDisburseLoan");
|
final Boolean multiDisburseLoan = rs.getBoolean("multiDisburseLoan");
|
||||||
|
final Boolean canDefineInstalmentAmount = rs.getBoolean("canDefineInstalmentAmount");
|
||||||
final BigDecimal outstandingLoanBalance = rs.getBigDecimal("outstandingLoanBalance");
|
final BigDecimal outstandingLoanBalance = rs.getBigDecimal("outstandingLoanBalance");
|
||||||
|
|
||||||
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
|
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
|
||||||
@@ -832,9 +834,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
||||||
interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment,
|
interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||||
graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged,
|
graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged,
|
||||||
syncDisbursementWithMeeting, loanCounter, loanProductCounter, multiDisburseLoan, fixedEmiAmount,
|
syncDisbursementWithMeeting, loanCounter, loanProductCounter, multiDisburseLoan, canDefineInstalmentAmount,
|
||||||
outstandingLoanBalance, inArrears, graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType,
|
fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing, isNPA, daysInMonthType,
|
||||||
isInterestRecalculationEnabled, interestRecalculationData, createStandingInstructionAtDisbursement);
|
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, createStandingInstructionAtDisbursement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -78,4 +78,8 @@ 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";
|
||||||
|
|
||||||
|
// Fixed installment configuration related
|
||||||
|
public static final String canDefineEmiAmountParamName = "canDefineInstalmentAmount";
|
||||||
|
public static final String instalmentAmountInMultiplesOfParamName = "instalmentAmountInMultiplesOf";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-4
@@ -84,6 +84,8 @@ public class LoanProductData {
|
|||||||
private final boolean isInterestRecalculationEnabled;
|
private final boolean isInterestRecalculationEnabled;
|
||||||
private final LoanProductInterestRecalculationData interestRecalculationData;
|
private final LoanProductInterestRecalculationData interestRecalculationData;
|
||||||
private final Integer minimumDaysBetweenDisbursalAndFirstRepayment;
|
private final Integer minimumDaysBetweenDisbursalAndFirstRepayment;
|
||||||
|
private final boolean canDefineInstalmentAmount;
|
||||||
|
private final Integer instalmentAmountInMultiplesOf;
|
||||||
|
|
||||||
// charges
|
// charges
|
||||||
private final Collection<ChargeData> charges;
|
private final Collection<ChargeData> charges;
|
||||||
@@ -193,6 +195,8 @@ public class LoanProductData {
|
|||||||
final boolean isInterestRecalculationEnabled = false;
|
final boolean isInterestRecalculationEnabled = false;
|
||||||
final LoanProductInterestRecalculationData interestRecalculationData = null;
|
final LoanProductInterestRecalculationData interestRecalculationData = null;
|
||||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = null;
|
||||||
|
final boolean canDefineInstalmentAmount = false;
|
||||||
|
final Integer instalmentAmountInMultiplesOf = 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,
|
||||||
@@ -202,7 +206,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,
|
||||||
|
canDefineInstalmentAmount, instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +263,8 @@ 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 boolean canDefineInstalmentAmount = false;
|
||||||
|
final Integer instalmentAmountInMultiplesOf = 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 +274,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,
|
||||||
|
canDefineInstalmentAmount, instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
|
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
|
||||||
@@ -330,6 +338,8 @@ 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 boolean canDefineInstalmentAmount = false;
|
||||||
|
final Integer instalmentAmountInMultiplesOf = 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 +350,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, canDefineInstalmentAmount, instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +382,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) {
|
LoanProductGuaranteeData loanProductGuaranteeData, boolean canDefineInstalmentAmount, Integer instalmentAmountInMultiplesOf) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
@@ -452,6 +462,9 @@ public class LoanProductData {
|
|||||||
this.rescheduleStrategyTypeOptions = null;
|
this.rescheduleStrategyTypeOptions = null;
|
||||||
this.interestRecalculationFrequencyTypeOptions = null;
|
this.interestRecalculationFrequencyTypeOptions = null;
|
||||||
|
|
||||||
|
this.canDefineInstalmentAmount = canDefineInstalmentAmount;
|
||||||
|
this.instalmentAmountInMultiplesOf = instalmentAmountInMultiplesOf;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
||||||
@@ -560,6 +573,8 @@ public class LoanProductData {
|
|||||||
this.interestRecalculationFrequencyTypeOptions = interestRecalculationFrequencyTypeOptions;
|
this.interestRecalculationFrequencyTypeOptions = interestRecalculationFrequencyTypeOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.canDefineInstalmentAmount = productData.canDefineInstalmentAmount;
|
||||||
|
this.instalmentAmountInMultiplesOf = productData.instalmentAmountInMultiplesOf;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||||
@@ -809,4 +824,8 @@ public class LoanProductData {
|
|||||||
if (isInterestRecalculationEnabled()) { return this.interestRecalculationData.getRecalculationRestFrequencyInterval(); }
|
if (isInterestRecalculationEnabled()) { return this.interestRecalculationData.getRecalculationRestFrequencyInterval(); }
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canDefineInstalmentAmount() {
|
||||||
|
return this.canDefineInstalmentAmount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+37
-2
@@ -136,6 +136,12 @@ 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 = "can_define_fixed_emi_amount")
|
||||||
|
private boolean canDefineInstalmentAmount;
|
||||||
|
|
||||||
|
@Column(name = "instalment_amount_in_multiples_of", nullable = true)
|
||||||
|
private Integer instalmentAmountInMultiplesOf;
|
||||||
|
|
||||||
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 +231,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
|
loanProductGuaranteeDetails = LoanProductGuaranteeDetails.createFrom(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final boolean canDefineEmiAmount = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canDefineEmiAmountParamName);
|
||||||
|
final Integer instalmentAmountInMultiplesOf = command
|
||||||
|
.integerValueOfParameterNamed(LoanProductConstants.instalmentAmountInMultiplesOfParamName);
|
||||||
|
|
||||||
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 +243,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, canDefineEmiAmount, instalmentAmountInMultiplesOf);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +468,8 @@ 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 boolean canDefineEmiAmount,
|
||||||
|
final Integer instalmentAmountInMultiplesOf) {
|
||||||
this.fund = fund;
|
this.fund = fund;
|
||||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||||
this.name = name.trim();
|
this.name = name.trim();
|
||||||
@@ -510,6 +521,8 @@ 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.canDefineInstalmentAmount = canDefineEmiAmount;
|
||||||
|
this.instalmentAmountInMultiplesOf = instalmentAmountInMultiplesOf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonetaryCurrency getCurrency() {
|
public MonetaryCurrency getCurrency() {
|
||||||
@@ -737,6 +750,20 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
this.loanProductGuaranteeDetails.update(command, actualChanges);
|
this.loanProductGuaranteeDetails.update(command, actualChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.canDefineEmiAmountParamName, this.canDefineInstalmentAmount)) {
|
||||||
|
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canDefineEmiAmountParamName);
|
||||||
|
actualChanges.put(LoanProductConstants.canDefineEmiAmountParamName, newValue);
|
||||||
|
this.canDefineInstalmentAmount = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInIntegerParameterNamedWithNullCheck(LoanProductConstants.instalmentAmountInMultiplesOfParamName,
|
||||||
|
this.instalmentAmountInMultiplesOf)) {
|
||||||
|
final Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.instalmentAmountInMultiplesOfParamName);
|
||||||
|
actualChanges.put(LoanProductConstants.instalmentAmountInMultiplesOfParamName, newValue);
|
||||||
|
actualChanges.put("locale", localeAsInput);
|
||||||
|
this.instalmentAmountInMultiplesOf = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
return actualChanges;
|
return actualChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -989,4 +1016,12 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
|||||||
return this.shortName;
|
return this.shortName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canDefineInstalmentAmount() {
|
||||||
|
return this.canDefineInstalmentAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInstalmentAmountInMultiplesOf() {
|
||||||
|
return this.instalmentAmountInMultiplesOf;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+30
-1
@@ -73,7 +73,8 @@ 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.canDefineEmiAmountParamName,
|
||||||
|
LoanProductConstants.instalmentAmountInMultiplesOfParamName));
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
@@ -325,6 +326,20 @@ public final class LoanProductDataValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.canDefineEmiAmountParamName, element)) {
|
||||||
|
final Boolean canDefineInstalmentAmount = this.fromApiJsonHelper.extractBooleanNamed(
|
||||||
|
LoanProductConstants.canDefineEmiAmountParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.canDefineEmiAmountParamName).value(canDefineInstalmentAmount)
|
||||||
|
.isOneOfTheseValues(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.instalmentAmountInMultiplesOfParamName, element)) {
|
||||||
|
final Integer instalmentAmountInMultiplesOf = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
|
||||||
|
LoanProductConstants.instalmentAmountInMultiplesOfParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.instalmentAmountInMultiplesOfParamName)
|
||||||
|
.value(instalmentAmountInMultiplesOf).ignoreIfNull().integerGreaterThanZero();
|
||||||
|
}
|
||||||
|
|
||||||
// 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 +763,20 @@ public final class LoanProductDataValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.canDefineEmiAmountParamName, element)) {
|
||||||
|
final Boolean canDefineInstalmentAmount = this.fromApiJsonHelper.extractBooleanNamed(
|
||||||
|
LoanProductConstants.canDefineEmiAmountParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.canDefineEmiAmountParamName).value(canDefineInstalmentAmount)
|
||||||
|
.isOneOfTheseValues(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.instalmentAmountInMultiplesOfParamName, element)) {
|
||||||
|
final Integer instalmentAmountInMultiplesOf = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
|
||||||
|
LoanProductConstants.instalmentAmountInMultiplesOfParamName, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.instalmentAmountInMultiplesOfParamName)
|
||||||
|
.value(instalmentAmountInMultiplesOf).ignoreIfNull().integerGreaterThanZero();
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -126,6 +126,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
+ "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, "
|
+ "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, "
|
||||||
+ "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, "
|
+ "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, "
|
||||||
+ "lp.days_in_month_enum as daysInMonth, lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as isInterestRecalculationEnabled, "
|
+ "lp.days_in_month_enum as daysInMonth, lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as isInterestRecalculationEnabled, "
|
||||||
|
+ "lp.can_define_fixed_emi_amount as canDefineInstalmentAmount, lp.instalment_amount_in_multiples_of as instalmentAmountInMultiplesOf, "
|
||||||
+ "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, "
|
+ "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, "
|
||||||
+ "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, "
|
||||||
@@ -240,6 +241,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
final EnumOptionData daysInMonthType = CommonEnumerations.daysInMonthType(daysInMonth);
|
final EnumOptionData daysInMonthType = CommonEnumerations.daysInMonthType(daysInMonth);
|
||||||
final int daysInYear = JdbcSupport.getInteger(rs, "daysInYear");
|
final int daysInYear = JdbcSupport.getInteger(rs, "daysInYear");
|
||||||
final EnumOptionData daysInYearType = CommonEnumerations.daysInYearType(daysInYear);
|
final EnumOptionData daysInYearType = CommonEnumerations.daysInYearType(daysInYear);
|
||||||
|
final Integer instalmentAmountInMultiplesOf = JdbcSupport.getInteger(rs, "instalmentAmountInMultiplesOf");
|
||||||
|
final boolean canDefineInstalmentAmount = rs.getBoolean("canDefineInstalmentAmount");
|
||||||
final boolean isInterestRecalculationEnabled = rs.getBoolean("isInterestRecalculationEnabled");
|
final boolean isInterestRecalculationEnabled = rs.getBoolean("isInterestRecalculationEnabled");
|
||||||
|
|
||||||
LoanProductInterestRecalculationData interestRecalculationData = null;
|
LoanProductInterestRecalculationData interestRecalculationData = null;
|
||||||
@@ -282,7 +285,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,
|
||||||
|
canDefineInstalmentAmount, instalmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE `m_product_loan`
|
||||||
|
ADD COLUMN `can_define_fixed_emi_amount` TINYINT(1) NOT NULL DEFAULT '0',
|
||||||
|
ADD COLUMN `instalment_amount_in_multiples_of` DECIMAL(19,6) NULL DEFAULT NULL;
|
||||||
Reference in New Issue
Block a user