Merge branch 'FINERACT-467' into develop
This commit is contained in:
+2
@@ -124,4 +124,6 @@ public interface LoanApiConstants {
|
|||||||
public static final String topupAmount = "topupAmount";
|
public static final String topupAmount = "topupAmount";
|
||||||
|
|
||||||
public static final String datatables = "datatables";
|
public static final String datatables = "datatables";
|
||||||
|
|
||||||
|
public static final String isEqualAmortizationParam = "isEqualAmortization";
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-20
@@ -213,6 +213,7 @@ public class LoanAccountData {
|
|||||||
private final Integer maximumGap;
|
private final Integer maximumGap;
|
||||||
|
|
||||||
private List<DatatableData> datatables = null;
|
private List<DatatableData> datatables = null;
|
||||||
|
private final Boolean isEqualAmortization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to produce a {@link LoanAccountData} with only collateral options
|
* Used to produce a {@link LoanAccountData} with only collateral options
|
||||||
@@ -331,7 +332,7 @@ public class LoanAccountData {
|
|||||||
final Long closureLoanId = null;
|
final Long closureLoanId = null;
|
||||||
final String closureLoanAccountNo = null;
|
final String closureLoanAccountNo = null;
|
||||||
final BigDecimal topupAmount = null;
|
final BigDecimal topupAmount = null;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||||
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
||||||
@@ -350,7 +351,7 @@ public class LoanAccountData {
|
|||||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
|
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,7 +472,7 @@ public class LoanAccountData {
|
|||||||
final Long closureLoanId = null;
|
final Long closureLoanId = null;
|
||||||
final String closureLoanAccountNo = null;
|
final String closureLoanAccountNo = null;
|
||||||
final BigDecimal topupAmount = null;
|
final BigDecimal topupAmount = null;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||||
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
||||||
@@ -490,7 +491,7 @@ public class LoanAccountData {
|
|||||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
|
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +521,7 @@ public class LoanAccountData {
|
|||||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -642,7 +643,7 @@ public class LoanAccountData {
|
|||||||
final Long closureLoanId = null;
|
final Long closureLoanId = null;
|
||||||
final String closureLoanAccountNo = null;
|
final String closureLoanAccountNo = null;
|
||||||
final BigDecimal topupAmount = null;
|
final BigDecimal topupAmount = null;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||||
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, principal,
|
||||||
@@ -661,7 +662,7 @@ public class LoanAccountData {
|
|||||||
maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
|
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,7 +692,7 @@ public class LoanAccountData {
|
|||||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -821,7 +822,6 @@ public class LoanAccountData {
|
|||||||
final Long closureLoanId = null;
|
final Long closureLoanId = null;
|
||||||
final String closureLoanAccountNo = null;
|
final String closureLoanAccountNo = null;
|
||||||
final BigDecimal topupAmount = null;
|
final BigDecimal topupAmount = null;
|
||||||
|
|
||||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||||
loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
|
loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
|
||||||
product.getFundId(), product.getFundName(), loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
product.getFundId(), product.getFundName(), loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||||
@@ -846,7 +846,7 @@ public class LoanAccountData {
|
|||||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||||
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
|
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
|
||||||
product.getMaximumGapBetweenInstallments(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId,
|
product.getMaximumGapBetweenInstallments(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId,
|
||||||
closureLoanAccountNo, topupAmount);
|
closureLoanAccountNo, topupAmount, product.isEqualAmortization());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||||
@@ -906,7 +906,7 @@ public class LoanAccountData {
|
|||||||
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
|
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
|
||||||
paidInAdvance, acc.interestRatesPeriods, product.isVariableInstallmentsAllowed(),
|
paidInAdvance, acc.interestRatesPeriods, product.isVariableInstallmentsAllowed(),
|
||||||
product.getMinimumGapBetweenInstallments(), product.getMaximumGapBetweenInstallments(), acc.subStatus, acc.canUseForTopup,
|
product.getMinimumGapBetweenInstallments(), product.getMaximumGapBetweenInstallments(), acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,7 +938,7 @@ public class LoanAccountData {
|
|||||||
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement,
|
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement,
|
||||||
final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus,
|
final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus,
|
||||||
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
|
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
|
||||||
final BigDecimal topupAmount) {
|
final BigDecimal topupAmount, final boolean isEqualAmortization) {
|
||||||
|
|
||||||
final LoanScheduleData repaymentSchedule = null;
|
final LoanScheduleData repaymentSchedule = null;
|
||||||
final Collection<LoanTransactionData> transactions = null;
|
final Collection<LoanTransactionData> transactions = null;
|
||||||
@@ -994,7 +994,7 @@ public class LoanAccountData {
|
|||||||
outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
|
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1047,7 +1047,7 @@ public class LoanAccountData {
|
|||||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup,
|
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup,
|
||||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||||
@@ -1089,7 +1089,7 @@ public class LoanAccountData {
|
|||||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||||
@@ -1154,7 +1154,7 @@ public class LoanAccountData {
|
|||||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,7 +1188,7 @@ public class LoanAccountData {
|
|||||||
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
|
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
||||||
@@ -1216,7 +1216,7 @@ public class LoanAccountData {
|
|||||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||||
@@ -1245,7 +1245,7 @@ public class LoanAccountData {
|
|||||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
|
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
|
||||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
|
||||||
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
|
||||||
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
|
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanAccountData(final Long id, //
|
private LoanAccountData(final Long id, //
|
||||||
@@ -1295,7 +1295,7 @@ public class LoanAccountData {
|
|||||||
final Collection<InterestRatePeriodData> interestRatesPeriods, final Boolean isVariableInstallmentsAllowed,
|
final Collection<InterestRatePeriodData> interestRatesPeriods, final Boolean isVariableInstallmentsAllowed,
|
||||||
final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus, final Boolean canUseForTopup,
|
final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus, final Boolean canUseForTopup,
|
||||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final boolean isTopup,
|
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final boolean isTopup,
|
||||||
final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount) {
|
final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization) {
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.accountNo = accountNo;
|
this.accountNo = accountNo;
|
||||||
@@ -1477,6 +1477,7 @@ public class LoanAccountData {
|
|||||||
this.closureLoanId = closureLoanId;
|
this.closureLoanId = closureLoanId;
|
||||||
this.closureLoanAccountNo = closureLoanAccountNo;
|
this.closureLoanAccountNo = closureLoanAccountNo;
|
||||||
this.topupAmount = topupAmount;
|
this.topupAmount = topupAmount;
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-3
@@ -115,6 +115,7 @@ import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerUnassignme
|
|||||||
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.UndoLastTrancheDisbursementException;
|
import org.apache.fineract.portfolio.loanaccount.exception.UndoLastTrancheDisbursementException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||||
@@ -2616,10 +2617,24 @@ public class Loan extends AbstractPersistableCustom<Long> {
|
|||||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
|
|
||||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
|
||||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO);
|
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||||
|
LoanScheduleGenerator loanScheduleGenerator = null;
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod);
|
if (loanApplicationTerms.isEqualAmortization()) {
|
||||||
|
if (loanApplicationTerms.getInterestMethod().isDecliningBalnce()) {
|
||||||
|
final LoanScheduleGenerator decliningLoanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(
|
||||||
|
InterestMethod.DECLINING_BALANCE);
|
||||||
|
Set<LoanCharge> loanCharges = charges();
|
||||||
|
LoanScheduleModel loanSchedule = decliningLoanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges, scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||||
|
|
||||||
|
loanApplicationTerms.updateTotalInterestDue(Money.of(loanApplicationTerms.getCurrency(), loanSchedule.getTotalInterestCharged()));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(InterestMethod.FLAT);
|
||||||
|
} else {
|
||||||
|
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getInterestMethod());
|
||||||
|
}
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||||
return loanSchedule;
|
return loanSchedule;
|
||||||
|
|||||||
+6
-3
@@ -67,7 +67,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
final LoanScheduleParams loanScheduleRecalculationDTO = null;
|
final LoanScheduleParams loanScheduleRecalculationDTO = null;
|
||||||
return generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, loanScheduleRecalculationDTO);
|
return generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, loanScheduleRecalculationDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final LoanScheduleParams loanScheduleParams) {
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final LoanScheduleParams loanScheduleParams) {
|
||||||
|
|
||||||
@@ -114,10 +114,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
// Determine the total interest owed over the full loan for FLAT
|
// Determine the total interest owed over the full loan for FLAT
|
||||||
// interest method .
|
// interest method .
|
||||||
if (!scheduleParams.isPartialUpdate()) {
|
if (!scheduleParams.isPartialUpdate() && !loanApplicationTerms.isEqualAmortization()) {
|
||||||
Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
|
Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
|
||||||
this.paymentPeriodsInOneYearCalculator, mc);
|
this.paymentPeriodsInOneYearCalculator, mc);
|
||||||
|
|
||||||
loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
|
loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isFirstRepayment = true;
|
boolean isFirstRepayment = true;
|
||||||
@@ -177,6 +179,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
isFirstRepayment = false;
|
isFirstRepayment = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!scheduleParams.getOutstandingBalance().isZero() || !scheduleParams.getDisburseDetailMap().isEmpty()) {
|
while (!scheduleParams.getOutstandingBalance().isZero() || !scheduleParams.getDisburseDetailMap().isEmpty()) {
|
||||||
LocalDate previousRepaymentDate = scheduleParams.getActualRepaymentDate();
|
LocalDate previousRepaymentDate = scheduleParams.getActualRepaymentDate();
|
||||||
scheduleParams.setActualRepaymentDate(this.scheduledDateGenerator.generateNextRepaymentDate(
|
scheduleParams.setActualRepaymentDate(this.scheduledDateGenerator.generateNextRepaymentDate(
|
||||||
|
|||||||
+56
-31
@@ -210,7 +210,7 @@ public final class LoanApplicationTerms {
|
|||||||
private Money totalInterestAccounted;
|
private Money totalInterestAccounted;
|
||||||
private int periodsCompleted = 0;
|
private int periodsCompleted = 0;
|
||||||
private int extraPeriods = 0;
|
private int extraPeriods = 0;
|
||||||
|
private boolean isEqualAmortization;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -234,7 +234,8 @@ public final class LoanApplicationTerms {
|
|||||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
||||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
||||||
Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, final Integer numberOfdays,
|
Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, final Integer numberOfdays,
|
||||||
boolean isSkipRepaymentOnFirstDayofMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod) {
|
boolean isSkipRepaymentOnFirstDayofMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
||||||
|
final boolean isEqualAmortization) {
|
||||||
|
|
||||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||||
@@ -249,7 +250,7 @@ public final class LoanApplicationTerms {
|
|||||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||||
preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
||||||
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO,
|
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO,
|
||||||
allowCompoundingOnEod);
|
allowCompoundingOnEod, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,6 +318,7 @@ public final class LoanApplicationTerms {
|
|||||||
final DaysInYearType daysInYearType = loanProductRelatedDetail.fetchDaysInYearType();
|
final DaysInYearType daysInYearType = loanProductRelatedDetail.fetchDaysInYearType();
|
||||||
final boolean isInterestRecalculationEnabled = loanProductRelatedDetail.isInterestRecalculationEnabled();
|
final boolean isInterestRecalculationEnabled = loanProductRelatedDetail.isInterestRecalculationEnabled();
|
||||||
final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
|
final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
|
||||||
|
final boolean isEqualAmortization = loanProductRelatedDetail.isEqualAmortization();
|
||||||
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
||||||
repaymentEvery, repaymentPeriodFrequencyType, ((nthDay != null) ? nthDay.getValue() : null), dayOfWeek, amortizationMethod,
|
repaymentEvery, repaymentPeriodFrequencyType, ((nthDay != null) ? nthDay.getValue() : null), dayOfWeek, amortizationMethod,
|
||||||
interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate,
|
interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate,
|
||||||
@@ -328,7 +330,7 @@ public final class LoanApplicationTerms {
|
|||||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||||
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount,
|
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount,
|
||||||
loanTermVariations, calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays,
|
loanTermVariations, calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays,
|
||||||
isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO, allowCompoundingOnEod);
|
isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||||
@@ -375,7 +377,7 @@ public final class LoanApplicationTerms {
|
|||||||
}
|
}
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||||
final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
|
final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
|
||||||
|
final boolean isEqualAmortization = loanProductRelatedDetail.isEqualAmortization();
|
||||||
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
||||||
repaymentEvery, repaymentPeriodFrequencyType, null, null, amortizationMethod, interestMethod, interestRatePerPeriod,
|
repaymentEvery, repaymentPeriodFrequencyType, null, null, amortizationMethod, interestMethod, interestRatePerPeriod,
|
||||||
interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||||
@@ -387,7 +389,7 @@ public final class LoanApplicationTerms {
|
|||||||
compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
||||||
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO,
|
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth, holidayDetailDTO,
|
||||||
allowCompoundingOnEod);
|
allowCompoundingOnEod, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +415,7 @@ public final class LoanApplicationTerms {
|
|||||||
applicationTerms.approvedPrincipal.getAmount(), loanTermVariations, applicationTerms.calendarHistoryDataWrapper,
|
applicationTerms.approvedPrincipal.getAmount(), loanTermVariations, applicationTerms.calendarHistoryDataWrapper,
|
||||||
applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled, applicationTerms.numberOfDays,
|
applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled, applicationTerms.numberOfDays,
|
||||||
applicationTerms.isSkipRepaymentOnFirstDayOfMonth, applicationTerms.holidayDetailDTO,
|
applicationTerms.isSkipRepaymentOnFirstDayOfMonth, applicationTerms.holidayDetailDTO,
|
||||||
applicationTerms.allowCompoundingOnEod);
|
applicationTerms.allowCompoundingOnEod, applicationTerms.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
@@ -437,7 +439,7 @@ public final class LoanApplicationTerms {
|
|||||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||||
final Integer numberOfdays, final boolean isSkipRepaymentOnFirstDayofMonth, final HolidayDetailDTO holidayDetailDTO,
|
final Integer numberOfdays, final boolean isSkipRepaymentOnFirstDayofMonth, final HolidayDetailDTO holidayDetailDTO,
|
||||||
final boolean allowCompoundingOnEod) {
|
final boolean allowCompoundingOnEod, final boolean isEqualAmortization) {
|
||||||
|
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.loanTermFrequency = loanTermFrequency;
|
this.loanTermFrequency = loanTermFrequency;
|
||||||
@@ -509,6 +511,7 @@ public final class LoanApplicationTerms {
|
|||||||
this.totalPrincipalAccountedForInterestCalcualtion = principal.zero();
|
this.totalPrincipalAccountedForInterestCalcualtion = principal.zero();
|
||||||
this.totalInterestAccounted = principal.zero();
|
this.totalInterestAccounted = principal.zero();
|
||||||
this.totalPrincipalAccounted = principal.zero();
|
this.totalPrincipalAccounted = principal.zero();
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||||
@@ -667,27 +670,36 @@ public final class LoanApplicationTerms {
|
|||||||
|
|
||||||
Money interestForInstallment = this.principal.zero();
|
Money interestForInstallment = this.principal.zero();
|
||||||
Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace.copy();
|
Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace.copy();
|
||||||
|
InterestMethod interestMethod = this.interestMethod;
|
||||||
|
|
||||||
switch (this.interestMethod) {
|
if (this.isEqualAmortization() && this.totalInterestDue != null) {
|
||||||
|
interestMethod = InterestMethod.FLAT;
|
||||||
|
}
|
||||||
|
switch (interestMethod) {
|
||||||
case FLAT:
|
case FLAT:
|
||||||
|
if (this.isEqualAmortization() && this.totalInterestDue != null && this.interestMethod.isDecliningBalnce()) {
|
||||||
switch (this.amortizationMethod) {
|
interestForInstallment = flatInterestPerInstallment(mc, this.totalInterestDue);
|
||||||
case EQUAL_INSTALLMENTS:
|
} else {
|
||||||
// average out outstanding interest over remaining
|
switch (this.amortizationMethod) {
|
||||||
// instalments where interest is applicable
|
case EQUAL_INSTALLMENTS:
|
||||||
interestForInstallment = calculateTotalFlatInterestForInstallmentAveragingOutGracePeriods(calculator, periodNumber,
|
// average out outstanding interest over remaining
|
||||||
mc);
|
// instalments where interest is applicable
|
||||||
break;
|
interestForInstallment = calculateTotalFlatInterestForInstallmentAveragingOutGracePeriods(calculator,
|
||||||
case EQUAL_PRINCIPAL:
|
periodNumber, mc);
|
||||||
// interest follows time-value of money and is brought
|
break;
|
||||||
// forward to next applicable interest payment period
|
case EQUAL_PRINCIPAL:
|
||||||
final PrincipalInterest result = calculateTotalFlatInterestForPeriod(calculator, periodNumber, mc,
|
// interest follows time-value of money and is
|
||||||
interestBroughtForwardDueToGrace);
|
// brought
|
||||||
interestForInstallment = result.interest();
|
// forward to next applicable interest payment
|
||||||
interestBroughtForwardDueToGrace = result.interestPaymentDueToGrace();
|
// period
|
||||||
break;
|
final PrincipalInterest result = calculateTotalFlatInterestForPeriod(calculator, periodNumber, mc,
|
||||||
case INVALID:
|
interestBroughtForwardDueToGrace);
|
||||||
break;
|
interestForInstallment = result.interest();
|
||||||
|
interestBroughtForwardDueToGrace = result.interestPaymentDueToGrace();
|
||||||
|
break;
|
||||||
|
case INVALID:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DECLINING_BALANCE:
|
case DECLINING_BALANCE:
|
||||||
@@ -868,13 +880,18 @@ public final class LoanApplicationTerms {
|
|||||||
private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
|
||||||
|
|
||||||
final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
|
final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
|
||||||
Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(this.actualNumberOfRepayments) - defaultToZeroIfNull(this.excludePeriodsForCalculation), mc.getRoundingMode());
|
return flatInterestPerInstallment(mc, totalInterestForLoanTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Money flatInterestPerInstallment(final MathContext mc, final Money totalInterestForLoanTerm) {
|
||||||
|
Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(this.actualNumberOfRepayments)
|
||||||
|
- defaultToZeroIfNull(this.excludePeriodsForCalculation), mc.getRoundingMode());
|
||||||
if (this.excludePeriodsForCalculation < this.periodsCompleted) {
|
if (this.excludePeriodsForCalculation < this.periodsCompleted) {
|
||||||
Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
|
Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
|
||||||
interestPerInstallment = interestLeft.dividedBy(Long.valueOf(this.actualNumberOfRepayments)
|
interestPerInstallment = interestLeft.dividedBy(Long.valueOf(this.actualNumberOfRepayments)
|
||||||
- defaultToZeroIfNull(this.periodsCompleted), mc.getRoundingMode());
|
- defaultToZeroIfNull(this.periodsCompleted), mc.getRoundingMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
return interestPerInstallment;
|
return interestPerInstallment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1320,7 +1337,7 @@ public final class LoanApplicationTerms {
|
|||||||
this.interestCalculationPeriodMethod, this.allowPartialPeriodInterestCalcualtion, this.repaymentEvery,
|
this.interestCalculationPeriodMethod, this.allowPartialPeriodInterestCalcualtion, this.repaymentEvery,
|
||||||
this.repaymentPeriodFrequencyType, this.numberOfRepayments, this.principalGrace, this.recurringMoratoriumOnPrincipalPeriods, this.interestPaymentGrace,
|
this.repaymentPeriodFrequencyType, this.numberOfRepayments, this.principalGrace, this.recurringMoratoriumOnPrincipalPeriods, this.interestPaymentGrace,
|
||||||
this.interestChargingGrace, this.amortizationMethod, this.inArrearsTolerance.getAmount(), this.graceOnArrearsAgeing,
|
this.interestChargingGrace, this.amortizationMethod, this.inArrearsTolerance.getAmount(), this.graceOnArrearsAgeing,
|
||||||
this.daysInMonthType.getValue(), this.daysInYearType.getValue(), this.interestRecalculationEnabled);
|
this.daysInMonthType.getValue(), this.daysInYearType.getValue(), this.interestRecalculationEnabled, this.isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getLoanTermFrequency() {
|
public Integer getLoanTermFrequency() {
|
||||||
@@ -1746,5 +1763,13 @@ public final class LoanApplicationTerms {
|
|||||||
public void setSeedDate(LocalDate seedDate) {
|
public void setSeedDate(LocalDate seedDate) {
|
||||||
this.seedDate = seedDate;
|
this.seedDate = seedDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEqualAmortization() {
|
||||||
|
return isEqualAmortization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEqualAmortization(boolean isEqualAmortization) {
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+7
@@ -120,4 +120,11 @@ public final class LoanScheduleModel {
|
|||||||
public BigDecimal getTotalPenaltyChargesCharged() {
|
public BigDecimal getTotalPenaltyChargesCharged() {
|
||||||
return this.totalPenaltyChargesCharged;
|
return this.totalPenaltyChargesCharged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getTotalInterestCharged() {
|
||||||
|
return this.totalInterestCharged;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+25
-4
@@ -89,6 +89,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.MinDaysBetweenDisbursalAndFirstRepaymentViolationException;
|
import org.apache.fineract.portfolio.loanaccount.exception.MinDaysBetweenDisbursalAndFirstRepaymentViolationException;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||||
@@ -201,6 +202,11 @@ public class LoanScheduleAssembler {
|
|||||||
|
|
||||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
|
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
|
||||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);
|
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);
|
||||||
|
|
||||||
|
boolean isEqualAmortization = false;
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isEqualAmortizationParam, element)) {
|
||||||
|
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isEqualAmortizationParam, element);
|
||||||
|
}
|
||||||
|
|
||||||
// interest terms
|
// interest terms
|
||||||
final Integer interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
|
final Integer interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
|
||||||
@@ -432,6 +438,8 @@ public class LoanScheduleAssembler {
|
|||||||
HolidayStatusType.ACTIVE.getValue());
|
HolidayStatusType.ACTIVE.getValue());
|
||||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
|
||||||
|
|
||||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
||||||
repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod,
|
repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod,
|
||||||
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||||
@@ -442,7 +450,7 @@ public class LoanScheduleAssembler {
|
|||||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
||||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(),
|
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(),
|
||||||
calendar, BigDecimal.ZERO, loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled,numberOfDays, isSkipMeetingOnFirstDay, detailDTO,
|
calendar, BigDecimal.ZERO, loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled,numberOfDays, isSkipMeetingOnFirstDay, detailDTO,
|
||||||
allowCompoundingOnEod);
|
allowCompoundingOnEod, isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
||||||
@@ -609,13 +617,26 @@ public class LoanScheduleAssembler {
|
|||||||
List<LoanDisbursementDetails> disbursementDetails) {
|
List<LoanDisbursementDetails> disbursementDetails) {
|
||||||
|
|
||||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element, disbursementDetails);
|
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element, disbursementDetails);
|
||||||
|
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
|
||||||
|
|
||||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
|
|
||||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
|
||||||
|
LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||||
|
if (loanApplicationTerms.isEqualAmortization()) {
|
||||||
|
if (loanApplicationTerms.getInterestMethod().isDecliningBalnce()) {
|
||||||
|
final LoanScheduleGenerator decliningLoanScheduleGenerator = this.loanScheduleFactory
|
||||||
|
.create(InterestMethod.DECLINING_BALANCE);
|
||||||
|
LoanScheduleModel loanSchedule = decliningLoanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges, detailDTO);
|
||||||
|
|
||||||
|
loanApplicationTerms.updateTotalInterestDue(Money.of(loanApplicationTerms.getCurrency(), loanSchedule.getTotalInterestCharged()));
|
||||||
|
|
||||||
|
}
|
||||||
|
loanScheduleGenerator = this.loanScheduleFactory.create(InterestMethod.FLAT);
|
||||||
|
} else {
|
||||||
|
loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||||
|
}
|
||||||
|
|
||||||
return loanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges, detailDTO);
|
return loanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges, detailDTO);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
|||||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
||||||
LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName,
|
LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName,
|
||||||
LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName,
|
LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName,
|
||||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables));
|
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam));
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
|
|
||||||
|
|||||||
+34
-3
@@ -37,7 +37,6 @@ import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterExc
|
|||||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||||
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||||
@@ -45,6 +44,7 @@ import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
|||||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -93,7 +93,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
|
LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
|
||||||
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
|
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
||||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables));
|
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam));
|
||||||
|
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
|
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
|
||||||
@@ -119,7 +119,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
final String loanTypeParameterName = "loanType";
|
final String loanTypeParameterName = "loanType";
|
||||||
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
|
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
|
||||||
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull();
|
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull();
|
||||||
|
|
||||||
if (!StringUtils.isBlank(loanTypeStr)) {
|
if (!StringUtils.isBlank(loanTypeStr)) {
|
||||||
final AccountType loanType = AccountType.fromName(loanTypeStr);
|
final AccountType loanType = AccountType.fromName(loanTypeStr);
|
||||||
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanType.getValue()).inMinMaxRange(1, 3);
|
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanType.getValue()).inMinMaxRange(1, 3);
|
||||||
@@ -160,6 +160,15 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isEqualAmortization = false;
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isEqualAmortizationParam, element)) {
|
||||||
|
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isEqualAmortizationParam, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanApiConstants.isEqualAmortizationParam).value(isEqualAmortization).ignoreIfNull()
|
||||||
|
.validateForBooleanValue();
|
||||||
|
if (isEqualAmortization && loanProduct.isInterestRecalculationEnabled()) { throw new EqualAmortizationUnsupportedFeatureException(
|
||||||
|
"interest.recalculation", "interest recalculation"); }
|
||||||
|
}
|
||||||
|
|
||||||
final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
|
final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
|
||||||
baseDataValidator.reset().parameter("productId").value(productId).notNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter("productId").value(productId).notNull().integerGreaterThanZero();
|
||||||
@@ -235,6 +244,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
.inMinMaxRange(0, 1);
|
.inMinMaxRange(0, 1);
|
||||||
|
|
||||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("floating.interest.rate",
|
||||||
|
"floating interest rate"); }
|
||||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||||
baseDataValidator
|
baseDataValidator
|
||||||
.reset()
|
.reset()
|
||||||
@@ -461,6 +472,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
|
unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
|
||||||
throw new UnsupportedParameterException(unsupportedParameterList);
|
throw new UnsupportedParameterException(unsupportedParameterList);
|
||||||
}
|
}
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("fixed.emi", "fixed emi"); }
|
||||||
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();
|
||||||
@@ -531,6 +543,15 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
|
final String accountNo = this.fromApiJsonHelper.extractStringNamed(accountNoParameterName, element);
|
||||||
baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).notBlank().notExceedingLengthOf(20);
|
baseDataValidator.reset().parameter(accountNoParameterName).value(accountNo).notBlank().notExceedingLengthOf(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isEqualAmortization = existingLoanApplication.getLoanProductRelatedDetail().isEqualAmortization();
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isEqualAmortizationParam, element)) {
|
||||||
|
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.isEqualAmortizationParam, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.isEqualAmortizationParam).value(isEqualAmortization).ignoreIfNull()
|
||||||
|
.validateForBooleanValue();
|
||||||
|
if (isEqualAmortization && loanProduct.isInterestRecalculationEnabled()) { throw new EqualAmortizationUnsupportedFeatureException(
|
||||||
|
"interest.recalculation", "interest recalculation"); }
|
||||||
|
}
|
||||||
|
|
||||||
final String externalIdParameterName = "externalId";
|
final String externalIdParameterName = "externalId";
|
||||||
if (this.fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
|
if (this.fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
|
||||||
@@ -632,6 +653,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("floating.interest.rate",
|
||||||
|
"floating interest rate"); }
|
||||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||||
baseDataValidator
|
baseDataValidator
|
||||||
.reset()
|
.reset()
|
||||||
@@ -907,6 +930,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
|
unsupportedParameterList.add(LoanApiConstants.emiAmountParameterName);
|
||||||
throw new UnsupportedParameterException(unsupportedParameterList);
|
throw new UnsupportedParameterException(unsupportedParameterList);
|
||||||
}
|
}
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("fixed.emi", "fixed emi"); }
|
||||||
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();
|
||||||
@@ -1076,11 +1100,18 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
|||||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.disbursementDataParameterName, element) && expectedDisbursement != null
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.disbursementDataParameterName, element) && expectedDisbursement != null
|
||||||
&& totalPrincipal != null) {
|
&& totalPrincipal != null) {
|
||||||
|
|
||||||
BigDecimal tatalDisbursement = BigDecimal.ZERO;
|
BigDecimal tatalDisbursement = BigDecimal.ZERO;
|
||||||
final JsonArray variationArray = this.fromApiJsonHelper.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName,
|
final JsonArray variationArray = this.fromApiJsonHelper.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName,
|
||||||
element);
|
element);
|
||||||
List<LocalDate> expectedDisbursementDates = new ArrayList<>();
|
List<LocalDate> expectedDisbursementDates = new ArrayList<>();
|
||||||
if (variationArray != null && variationArray.size() > 0) {
|
if (variationArray != null && variationArray.size() > 0) {
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isEqualAmortizationParam, element)) {
|
||||||
|
boolean isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isEqualAmortizationParam,
|
||||||
|
element);
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("tranche.disbursal",
|
||||||
|
"tranche disbursal"); }
|
||||||
|
}
|
||||||
int i = 0;
|
int i = 0;
|
||||||
do {
|
do {
|
||||||
final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
|
final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
|
||||||
|
|||||||
+3
-3
@@ -563,7 +563,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
+ " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, "
|
+ " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, "
|
||||||
+ " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, "
|
+ " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, "
|
||||||
+ " l.term_frequency as termFrequency, l.term_period_frequency_enum as termPeriodFrequencyType, "
|
+ " l.term_frequency as termFrequency, l.term_period_frequency_enum as termPeriodFrequencyType, "
|
||||||
+ " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.interest_calculated_in_period_enum as interestCalculationPeriodType,"
|
+ " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.is_equal_amortization as isEqualAmortization, l.interest_calculated_in_period_enum as interestCalculationPeriodType,"
|
||||||
+ " l.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion,"
|
+ " l.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion,"
|
||||||
+ " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, "
|
+ " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, "
|
||||||
+ " lps.name as transactionStrategyName, "
|
+ " lps.name as transactionStrategyName, "
|
||||||
@@ -790,7 +790,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
final int amortizationTypeInt = JdbcSupport.getInteger(rs, "amortizationType");
|
final int amortizationTypeInt = JdbcSupport.getInteger(rs, "amortizationType");
|
||||||
final int interestTypeInt = JdbcSupport.getInteger(rs, "interestType");
|
final int interestTypeInt = JdbcSupport.getInteger(rs, "interestType");
|
||||||
final int interestCalculationPeriodTypeInt = JdbcSupport.getInteger(rs, "interestCalculationPeriodType");
|
final int interestCalculationPeriodTypeInt = JdbcSupport.getInteger(rs, "interestCalculationPeriodType");
|
||||||
|
final boolean isEqualAmortization = rs.getBoolean("isEqualAmortization");
|
||||||
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeInt);
|
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeInt);
|
||||||
final EnumOptionData interestType = LoanEnumerations.interestType(interestTypeInt);
|
final EnumOptionData interestType = LoanEnumerations.interestType(interestTypeInt);
|
||||||
final EnumOptionData interestCalculationPeriodType = LoanEnumerations
|
final EnumOptionData interestCalculationPeriodType = LoanEnumerations
|
||||||
@@ -965,7 +965,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
loanProductCounter, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears,
|
loanProductCounter, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears,
|
||||||
graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
interestRecalculationData, createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap,
|
interestRecalculationData, createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap,
|
||||||
maximumGap, loanSubStatus, canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
|
maximumGap, loanSubStatus, canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -132,5 +132,7 @@ public interface LoanProductConstants {
|
|||||||
|
|
||||||
public static final String canUseForTopup = "canUseForTopup";
|
public static final String canUseForTopup = "canUseForTopup";
|
||||||
|
|
||||||
|
public static final String isEqualAmortizationParam = "isEqualAmortization";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -91,7 +91,7 @@ public class LoanProductsApiResource {
|
|||||||
"interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
|
"interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
|
||||||
"accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates",
|
"accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates",
|
||||||
"floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate", "defaultDifferentialLendingRate",
|
"floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate", "defaultDifferentialLendingRate",
|
||||||
"maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed", LoanProductConstants.canUseForTopup));
|
"maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed", LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam));
|
||||||
|
|
||||||
private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
|
private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
|
||||||
"allowedProducts", "productOptions"));
|
"allowedProducts", "productOptions"));
|
||||||
|
|||||||
+18
-7
@@ -183,6 +183,7 @@ public class LoanProductData {
|
|||||||
private final Boolean accountMovesOutOfNPAOnlyOnArrearsCompletion;
|
private final Boolean accountMovesOutOfNPAOnlyOnArrearsCompletion;
|
||||||
private LoanProductConfigurableAttributes allowAttributeOverrides;
|
private LoanProductConfigurableAttributes allowAttributeOverrides;
|
||||||
private final boolean syncExpectedWithDisbursementDate;
|
private final boolean syncExpectedWithDisbursementDate;
|
||||||
|
private final boolean isEqualAmortization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used when returning lookup information about loan product for dropdowns.
|
* Used when returning lookup information about loan product for dropdowns.
|
||||||
@@ -258,7 +259,7 @@ public class LoanProductData {
|
|||||||
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||||
final boolean syncExpectedWithDisbursementDate = false;
|
final boolean syncExpectedWithDisbursementDate = false;
|
||||||
final boolean canUseForTopup = false;
|
final boolean canUseForTopup = false;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||||
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
|
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType,
|
||||||
@@ -273,7 +274,7 @@ public class LoanProductData {
|
|||||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
||||||
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
||||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||||
syncExpectedWithDisbursementDate, canUseForTopup);
|
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +351,7 @@ public class LoanProductData {
|
|||||||
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||||
final boolean syncExpectedWithDisbursementDate = false;
|
final boolean syncExpectedWithDisbursementDate = false;
|
||||||
final boolean canUseForTopup = false;
|
final boolean canUseForTopup = false;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -365,7 +367,7 @@ public class LoanProductData {
|
|||||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
||||||
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
||||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||||
syncExpectedWithDisbursementDate, canUseForTopup);
|
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,6 +451,7 @@ public class LoanProductData {
|
|||||||
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||||
final boolean syncExpectedWithDisbursementDate = false;
|
final boolean syncExpectedWithDisbursementDate = false;
|
||||||
final boolean canUseForTopup = false;
|
final boolean canUseForTopup = false;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -464,7 +467,7 @@ public class LoanProductData {
|
|||||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||||
syncExpectedWithDisbursementDate, canUseForTopup);
|
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,6 +545,7 @@ public class LoanProductData {
|
|||||||
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||||
final boolean syncExpectedWithDisbursementDate = false;
|
final boolean syncExpectedWithDisbursementDate = false;
|
||||||
final boolean canUseForTopup = false;
|
final boolean canUseForTopup = false;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -557,7 +561,7 @@ public class LoanProductData {
|
|||||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||||
syncExpectedWithDisbursementDate, canUseForTopup);
|
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,7 +575,7 @@ public class LoanProductData {
|
|||||||
productData.penaltyToIncomeAccountMappings = penaltyToGLAccountMappings;
|
productData.penaltyToIncomeAccountMappings = penaltyToGLAccountMappings;
|
||||||
return productData;
|
return productData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanProductData(final Long id, final String name, final String shortName, final String description, final CurrencyData currency,
|
public LoanProductData(final Long id, final String name, final String shortName, final String description, final CurrencyData currency,
|
||||||
final BigDecimal principal, final BigDecimal minPrincipal, final BigDecimal maxPrincipal, final BigDecimal tolerance,
|
final BigDecimal principal, final BigDecimal minPrincipal, final BigDecimal maxPrincipal, final BigDecimal tolerance,
|
||||||
final Integer numberOfRepayments, final Integer minNumberOfRepayments, final Integer maxNumberOfRepayments,
|
final Integer numberOfRepayments, final Integer minNumberOfRepayments, final Integer maxNumberOfRepayments,
|
||||||
@@ -596,7 +600,7 @@ public class LoanProductData {
|
|||||||
BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
|
BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
|
||||||
boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
|
boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
|
||||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup) {
|
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
@@ -700,6 +704,7 @@ public class LoanProductData {
|
|||||||
this.preClosureInterestCalculationStrategyOptions = null;
|
this.preClosureInterestCalculationStrategyOptions = null;
|
||||||
this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate;
|
this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate;
|
||||||
this.canUseForTopup = canUseForTopup;
|
this.canUseForTopup = canUseForTopup;
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -836,6 +841,7 @@ public class LoanProductData {
|
|||||||
this.preClosureInterestCalculationStrategyOptions = preCloseInterestCalculationStrategyOptions;
|
this.preClosureInterestCalculationStrategyOptions = preCloseInterestCalculationStrategyOptions;
|
||||||
this.syncExpectedWithDisbursementDate = productData.syncExpectedWithDisbursementDate;
|
this.syncExpectedWithDisbursementDate = productData.syncExpectedWithDisbursementDate;
|
||||||
this.canUseForTopup = productData.canUseForTopup;
|
this.canUseForTopup = productData.canUseForTopup;
|
||||||
|
this.isEqualAmortization = productData.isEqualAmortization;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||||
@@ -1196,4 +1202,9 @@ public class LoanProductData {
|
|||||||
public BigDecimal getInterestRateDifferential() {
|
public BigDecimal getInterestRateDifferential() {
|
||||||
return this.interestRateDifferential;
|
return this.interestRateDifferential;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEqualAmortization() {
|
||||||
|
return isEqualAmortization;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+18
-3
@@ -182,6 +182,9 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
|
|
||||||
@Column(name = "can_use_for_topup", nullable = false)
|
@Column(name = "can_use_for_topup", nullable = false)
|
||||||
private boolean canUseForTopup = false;
|
private boolean canUseForTopup = false;
|
||||||
|
|
||||||
|
@Column(name = "is_equal_amortization", nullable = false)
|
||||||
|
private boolean isEqualAmortization = false;
|
||||||
|
|
||||||
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, FloatingRate floatingRate) {
|
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) {
|
||||||
@@ -331,6 +334,9 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
final boolean canUseForTopup = command.parameterExists(LoanProductConstants.canUseForTopup)
|
final boolean canUseForTopup = command.parameterExists(LoanProductConstants.canUseForTopup)
|
||||||
? command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canUseForTopup)
|
? command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canUseForTopup)
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
|
final boolean isEqualAmortization = command.parameterExists(LoanProductConstants.isEqualAmortizationParam) ? command
|
||||||
|
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.isEqualAmortizationParam) : false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -345,7 +351,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
installmentAmountInMultiplesOf, loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate,
|
installmentAmountInMultiplesOf, loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate,
|
||||||
interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
|
interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGapBetweenInstallments,
|
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGapBetweenInstallments,
|
||||||
maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup);
|
maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,7 +581,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||||
Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
|
Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
|
||||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup) {
|
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization) {
|
||||||
this.fund = fund;
|
this.fund = fund;
|
||||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||||
this.name = name.trim();
|
this.name = name.trim();
|
||||||
@@ -607,7 +613,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
|
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
|
||||||
considerPartialPeriodInterest, repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods,
|
considerPartialPeriodInterest, repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods,
|
||||||
graceOnInterestPayment, graceOnInterestCharged, amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing,
|
graceOnInterestPayment, graceOnInterestCharged, amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing,
|
||||||
daysInMonthType.getValue(), daysInYearType.getValue(), isInterestRecalculationEnabled);
|
daysInMonthType.getValue(), daysInYearType.getValue(), isInterestRecalculationEnabled, isEqualAmortization);
|
||||||
|
|
||||||
this.loanProductRelatedDetail.validateRepaymentPeriodWithGraceSettings();
|
this.loanProductRelatedDetail.validateRepaymentPeriodWithGraceSettings();
|
||||||
|
|
||||||
@@ -652,6 +658,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
this.syncExpectedWithDisbursementDate =
|
this.syncExpectedWithDisbursementDate =
|
||||||
syncExpectedWithDisbursementDate;
|
syncExpectedWithDisbursementDate;
|
||||||
this.canUseForTopup = canUseForTopup;
|
this.canUseForTopup = canUseForTopup;
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonetaryCurrency getCurrency() {
|
public MonetaryCurrency getCurrency() {
|
||||||
@@ -1365,4 +1372,12 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
return this.canUseForTopup;
|
return this.canUseForTopup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEqualAmortization() {
|
||||||
|
return isEqualAmortization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEqualAmortization(boolean isEqualAmortization) {
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+21
-3
@@ -127,6 +127,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
|
|
||||||
@Column(name = "interest_recalculation_enabled")
|
@Column(name = "interest_recalculation_enabled")
|
||||||
private boolean isInterestRecalculationEnabled;
|
private boolean isInterestRecalculationEnabled;
|
||||||
|
|
||||||
|
@Column(name = "is_equal_amortization", nullable = false)
|
||||||
|
private boolean isEqualAmortization = false;
|
||||||
|
|
||||||
public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal,
|
public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal,
|
||||||
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
|
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
|
||||||
@@ -135,13 +138,13 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
|
final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
|
||||||
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
||||||
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing,
|
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing,
|
||||||
final Integer daysInMonthType, final Integer daysInYearType, final boolean isInterestRecalculationEnabled) {
|
final Integer daysInMonthType, final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization) {
|
||||||
|
|
||||||
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
|
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
|
||||||
nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion,
|
nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion,
|
||||||
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment,
|
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment,
|
||||||
graceOnInterestCharged, amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
graceOnInterestCharged, amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
||||||
isInterestRecalculationEnabled);
|
isInterestRecalculationEnabled, isEqualAmortization);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LoanProductRelatedDetail() {
|
protected LoanProductRelatedDetail() {
|
||||||
@@ -155,7 +158,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfRepayments,
|
final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfRepayments,
|
||||||
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
||||||
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing,
|
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing,
|
||||||
final Integer daysInMonthType, final Integer daysInYearType, final boolean isInterestRecalculationEnabled) {
|
final Integer daysInMonthType, final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization) {
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.principal = defaultPrincipal;
|
this.principal = defaultPrincipal;
|
||||||
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
|
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
|
||||||
@@ -181,6 +184,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
this.daysInMonthType = daysInMonthType;
|
this.daysInMonthType = daysInMonthType;
|
||||||
this.daysInYearType = daysInYearType;
|
this.daysInYearType = daysInYearType;
|
||||||
this.isInterestRecalculationEnabled = isInterestRecalculationEnabled;
|
this.isInterestRecalculationEnabled = isInterestRecalculationEnabled;
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer defaultToNullIfZero(final Integer value) {
|
private Integer defaultToNullIfZero(final Integer value) {
|
||||||
@@ -493,6 +497,12 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
actualChanges.put(LoanProductConstants.isInterestRecalculationEnabledParameterName, newValue);
|
actualChanges.put(LoanProductConstants.isInterestRecalculationEnabledParameterName, newValue);
|
||||||
this.isInterestRecalculationEnabled = newValue;
|
this.isInterestRecalculationEnabled = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.isEqualAmortizationParam, this.isEqualAmortization)) {
|
||||||
|
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.isEqualAmortizationParam);
|
||||||
|
actualChanges.put(LoanProductConstants.isEqualAmortizationParam, newValue);
|
||||||
|
this.isEqualAmortization = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
validateRepaymentPeriodWithGraceSettings();
|
validateRepaymentPeriodWithGraceSettings();
|
||||||
|
|
||||||
@@ -648,5 +658,13 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
public boolean isAllowPartialPeriodInterestCalcualtion() {
|
public boolean isAllowPartialPeriodInterestCalcualtion() {
|
||||||
return this.allowPartialPeriodInterestCalcualtion;
|
return this.allowPartialPeriodInterestCalcualtion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEqualAmortization() {
|
||||||
|
return isEqualAmortization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEqualAmortization(boolean isEqualAmortization) {
|
||||||
|
this.isEqualAmortization = isEqualAmortization;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanproduct.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class EqualAmortizationUnsupportedFeatureException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
public EqualAmortizationUnsupportedFeatureException(String code, String property) {
|
||||||
|
super("error.msg.equal.amortization.does.not.support." + code, "Equal Amortization does not support " + property, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+40
-3
@@ -45,9 +45,9 @@ import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
|||||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
|
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.exception.EqualAmortizationUnsupportedFeatureException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ public final class LoanProductDataValidator {
|
|||||||
LoanProductConstants.recalculationRestFrequencyWeekdayParamName,
|
LoanProductConstants.recalculationRestFrequencyWeekdayParamName,
|
||||||
LoanProductConstants.recalculationRestFrequencyNthDayParamName, LoanProductConstants.recalculationRestFrequencyOnDayParamName,
|
LoanProductConstants.recalculationRestFrequencyNthDayParamName, LoanProductConstants.recalculationRestFrequencyOnDayParamName,
|
||||||
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName,
|
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName,
|
||||||
LoanProductConstants.canUseForTopup));
|
LoanProductConstants.canUseForTopup, LoanProductConstants.isEqualAmortizationParam));
|
||||||
|
|
||||||
private static final String[] supportedloanConfigurableAttributes = {LoanProductConstants.amortizationTypeParamName,
|
private static final String[] supportedloanConfigurableAttributes = {LoanProductConstants.amortizationTypeParamName,
|
||||||
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName,
|
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName,
|
||||||
@@ -148,6 +148,13 @@ public final class LoanProductDataValidator {
|
|||||||
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
|
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
|
||||||
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
|
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isEqualAmortization = false;
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isEqualAmortizationParam, element)) {
|
||||||
|
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.isEqualAmortizationParam, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.isEqualAmortizationParam).value(isEqualAmortization).ignoreIfNull()
|
||||||
|
.validateForBooleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, element)) {
|
||||||
final Long minimumDaysBetweenDisbursalAndFirstRepayment = this.fromApiJsonHelper.extractLongNamed(
|
final Long minimumDaysBetweenDisbursalAndFirstRepayment = this.fromApiJsonHelper.extractLongNamed(
|
||||||
@@ -314,6 +321,8 @@ public final class LoanProductDataValidator {
|
|||||||
|
|
||||||
if (isInterestRecalculationEnabled != null) {
|
if (isInterestRecalculationEnabled != null) {
|
||||||
if (isInterestRecalculationEnabled.booleanValue()) {
|
if (isInterestRecalculationEnabled.booleanValue()) {
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("interest.recalculation",
|
||||||
|
"interest recalculation"); }
|
||||||
validateInterestRecalculationParams(element, baseDataValidator, null);
|
validateInterestRecalculationParams(element, baseDataValidator, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,7 +330,9 @@ public final class LoanProductDataValidator {
|
|||||||
// interest rates
|
// interest rates
|
||||||
if (this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)
|
if (this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)
|
||||||
&& this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true) {
|
&& this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true) {
|
||||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("floating.interest.rate",
|
||||||
|
"floating interest rate"); }
|
||||||
|
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||||
baseDataValidator
|
baseDataValidator
|
||||||
.reset()
|
.reset()
|
||||||
.parameter("interestRatePerPeriod")
|
.parameter("interestRatePerPeriod")
|
||||||
@@ -641,6 +652,13 @@ public final class LoanProductDataValidator {
|
|||||||
private void validateVariableInstallmentSettings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
private void validateVariableInstallmentSettings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowVariableInstallmentsParamName, element)
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.allowVariableInstallmentsParamName, element)
|
||||||
&& this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.allowVariableInstallmentsParamName, element)) {
|
&& this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.allowVariableInstallmentsParamName, element)) {
|
||||||
|
|
||||||
|
boolean isEqualAmortization = false;
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isEqualAmortizationParam, element)) {
|
||||||
|
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.isEqualAmortizationParam, element);
|
||||||
|
}
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("variable.installment",
|
||||||
|
"variable installment"); }
|
||||||
|
|
||||||
Long minimumGapBetweenInstallments = null;
|
Long minimumGapBetweenInstallments = null;
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGapBetweenInstallments, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumGapBetweenInstallments, element)) {
|
||||||
@@ -721,6 +739,13 @@ public final class LoanProductDataValidator {
|
|||||||
baseDataValidator.reset().parameter(LoanProductConstants.multiDisburseLoanParameterName).value(multiDisburseLoan)
|
baseDataValidator.reset().parameter(LoanProductConstants.multiDisburseLoanParameterName).value(multiDisburseLoan)
|
||||||
.ignoreIfNull().validateForBooleanValue();
|
.ignoreIfNull().validateForBooleanValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isEqualAmortization = false;
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isEqualAmortizationParam, element)) {
|
||||||
|
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.isEqualAmortizationParam, element);
|
||||||
|
}
|
||||||
|
if (isEqualAmortization && multiDisburseLoan) { throw new EqualAmortizationUnsupportedFeatureException("tranche.disbursal",
|
||||||
|
"tranche disbursal"); }
|
||||||
|
|
||||||
if (multiDisburseLoan) {
|
if (multiDisburseLoan) {
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.outstandingLoanBalanceParameterName, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.outstandingLoanBalanceParameterName, element)) {
|
||||||
@@ -1114,6 +1139,13 @@ public final class LoanProductDataValidator {
|
|||||||
baseDataValidator.reset().parameter(LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName)
|
baseDataValidator.reset().parameter(LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName)
|
||||||
.value(npaChangeConfig).notNull().isOneOfTheseValues(true, false);
|
.value(npaChangeConfig).notNull().isOneOfTheseValues(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isEqualAmortization = loanProduct.isEqualAmortization();
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.isEqualAmortizationParam, element)) {
|
||||||
|
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.isEqualAmortizationParam, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.isEqualAmortizationParam).value(isEqualAmortization).ignoreIfNull()
|
||||||
|
.validateForBooleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
// Interest recalculation settings
|
// Interest recalculation settings
|
||||||
Boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled();
|
Boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled();
|
||||||
@@ -1126,6 +1158,8 @@ public final class LoanProductDataValidator {
|
|||||||
|
|
||||||
if (isInterestRecalculationEnabled != null) {
|
if (isInterestRecalculationEnabled != null) {
|
||||||
if (isInterestRecalculationEnabled) {
|
if (isInterestRecalculationEnabled) {
|
||||||
|
if (isEqualAmortization) { throw new EqualAmortizationUnsupportedFeatureException("interest.recalculation",
|
||||||
|
"interest recalculation"); }
|
||||||
validateInterestRecalculationParams(element, baseDataValidator, loanProduct);
|
validateInterestRecalculationParams(element, baseDataValidator, loanProduct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1136,6 +1170,9 @@ public final class LoanProductDataValidator {
|
|||||||
isLinkedToFloatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element);
|
isLinkedToFloatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element);
|
||||||
}
|
}
|
||||||
if (isLinkedToFloatingInterestRates) {
|
if (isLinkedToFloatingInterestRates) {
|
||||||
|
if(isEqualAmortization){
|
||||||
|
throw new EqualAmortizationUnsupportedFeatureException("floating.interest.rate", "floating interest rate");
|
||||||
|
}
|
||||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||||
baseDataValidator
|
baseDataValidator
|
||||||
.reset()
|
.reset()
|
||||||
|
|||||||
+4
-3
@@ -227,7 +227,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
+ "lp.allow_variabe_installments as isVariableIntallmentsAllowed, "
|
+ "lp.allow_variabe_installments as isVariableIntallmentsAllowed, "
|
||||||
+ "lvi.minimum_gap as minimumGap, "
|
+ "lvi.minimum_gap as minimumGap, "
|
||||||
+ "lvi.maximum_gap as maximumGap, "
|
+ "lvi.maximum_gap as maximumGap, "
|
||||||
+ "lp.can_use_for_topup as canUseForTopup "
|
+ "lp.can_use_for_topup as canUseForTopup, lp.is_equal_amortization as isEqualAmortization "
|
||||||
+ " from m_product_loan lp "
|
+ " from m_product_loan lp "
|
||||||
+ " left join m_fund f on f.id = lp.fund_id "
|
+ " left join m_fund f on f.id = lp.fund_id "
|
||||||
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
|
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
|
||||||
@@ -308,7 +308,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
|
|
||||||
final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod");
|
final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod");
|
||||||
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId);
|
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId);
|
||||||
|
final boolean isEqualAmortization = rs.getBoolean("isEqualAmortization");
|
||||||
|
|
||||||
final Integer interestRateFrequencyTypeId = JdbcSupport.getInteger(rs, "interestRatePerPeriodFreq");
|
final Integer interestRateFrequencyTypeId = JdbcSupport.getInteger(rs, "interestRatePerPeriodFreq");
|
||||||
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeId);
|
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeId);
|
||||||
|
|
||||||
@@ -464,7 +465,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
|
installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
|
||||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
|
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
|
||||||
maximumGap, syncExpectedWithDisbursementDate, canUseForTopup);
|
maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
--
|
||||||
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
-- or more contributor license agreements. See the NOTICE file
|
||||||
|
-- distributed with this work for additional information
|
||||||
|
-- regarding copyright ownership. The ASF licenses this file
|
||||||
|
-- to you under the Apache License, Version 2.0 (the
|
||||||
|
-- "License"); you may not use this file except in compliance
|
||||||
|
-- with the License. You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing,
|
||||||
|
-- software distributed under the License is distributed on an
|
||||||
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
-- KIND, either express or implied. See the License for the
|
||||||
|
-- specific language governing permissions and limitations
|
||||||
|
-- under the License.
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE `m_product_loan` ADD COLUMN `is_equal_amortization` TINYINT(1) NOT NULL DEFAULT '0';
|
||||||
|
ALTER TABLE `m_loan` ADD COLUMN `is_equal_amortization` TINYINT(1) NOT NULL DEFAULT '0';
|
||||||
+2
-2
@@ -153,10 +153,10 @@ public class LoanProductRelatedDetailTestHelper {
|
|||||||
final Integer daysInYearType = DaysInYearType.ACTUAL.getValue();
|
final Integer daysInYearType = DaysInYearType.ACTUAL.getValue();
|
||||||
final boolean isInterestRecalculationEnabled = false;
|
final boolean isInterestRecalculationEnabled = false;
|
||||||
final boolean considerPartialPeriodInterest = false;
|
final boolean considerPartialPeriodInterest = false;
|
||||||
|
final boolean isEqualAmortization = false;
|
||||||
return new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
|
return new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType,
|
||||||
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, considerPartialPeriodInterest, repayEvery,
|
defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, considerPartialPeriodInterest, repayEvery,
|
||||||
repaymentFrequencyType, defaultNumberOfRepayments, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged,
|
repaymentFrequencyType, defaultNumberOfRepayments, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged,
|
||||||
amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled);
|
amortizationMethod, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, isEqualAmortization);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user