[FINERACT-1348] -balloon-payment-fixed-principal-percentage (#1702)
This commit is contained in:
+2
@@ -135,4 +135,6 @@ public interface LoanApiConstants {
|
||||
String applicationId = "applicationId";
|
||||
String lastApplication = "lastApplication";
|
||||
|
||||
String fixedPrincipalPercentagePerInstallmentParamName = "fixedPrincipalPercentagePerInstallment";
|
||||
|
||||
}
|
||||
|
||||
+7
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -462,6 +463,8 @@ final class LoansApiResourceSwagger {
|
||||
@Schema(example = "24")
|
||||
public Integer annualInterestRate;
|
||||
public GetLoansLoanIdAmortizationType amortizationType;
|
||||
@Schema(example = "5.5")
|
||||
public BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
public GetLoansLoanIdInterestType interestType;
|
||||
public GetLoansLoanIdInterestCalculationPeriodType interestCalculationPeriodType;
|
||||
@Schema(example = "2")
|
||||
@@ -507,6 +510,8 @@ final class LoansApiResourceSwagger {
|
||||
public Integer interestRatePerPeriod;
|
||||
@Schema(example = "1")
|
||||
public Integer amortizationType;
|
||||
@Schema(example = "5.5")
|
||||
public BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
@Schema(example = "0")
|
||||
public Integer interestType;
|
||||
@Schema(example = "1")
|
||||
@@ -611,6 +616,8 @@ final class LoansApiResourceSwagger {
|
||||
public Integer interestCalculationPeriodType;
|
||||
@Schema(example = "1")
|
||||
public Integer amortizationType;
|
||||
@Schema(example = "5.5")
|
||||
public BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
@Schema(example = "04 March 2014")
|
||||
public String expectedDisbursementDate;
|
||||
@Schema(example = "1")
|
||||
|
||||
+35
-17
@@ -219,6 +219,7 @@ public final class LoanAccountData {
|
||||
|
||||
private List<DatatableData> datatables = null;
|
||||
private final Boolean isEqualAmortization;
|
||||
private final BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
// Rate
|
||||
private final List<RateData> rates;
|
||||
@@ -421,6 +422,7 @@ public final class LoanAccountData {
|
||||
this.maximumGap = null;
|
||||
this.isEqualAmortization = null;
|
||||
this.isRatesEnabled = false;
|
||||
this.fixedPrincipalPercentagePerInstallment = null;
|
||||
}
|
||||
|
||||
public Integer getRowIndex() {
|
||||
@@ -585,6 +587,7 @@ public final class LoanAccountData {
|
||||
final boolean isEqualAmortization = false;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -605,7 +608,8 @@ public final class LoanAccountData {
|
||||
inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||
isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup,
|
||||
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled);
|
||||
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -729,6 +733,7 @@ public final class LoanAccountData {
|
||||
final boolean isEqualAmortization = false;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -749,7 +754,8 @@ public final class LoanAccountData {
|
||||
overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||
isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup,
|
||||
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled);
|
||||
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -780,7 +786,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -905,6 +912,7 @@ public final class LoanAccountData {
|
||||
final boolean isEqualAmortization = false;
|
||||
final List<RateData> rates = null;
|
||||
final Boolean isRatesEnabled = false;
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -925,7 +933,8 @@ public final class LoanAccountData {
|
||||
overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||
isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup,
|
||||
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled);
|
||||
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -955,7 +964,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -1122,7 +1132,8 @@ public final class LoanAccountData {
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
|
||||
product.getMaximumGapBetweenInstallments(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId,
|
||||
closureLoanAccountNo, topupAmount, product.isEqualAmortization(), rates, isRatesEnabled);
|
||||
closureLoanAccountNo, topupAmount, product.isEqualAmortization(), rates, isRatesEnabled,
|
||||
product.getFixedPrincipalPercentagePerInstallment());
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -1190,7 +1201,8 @@ public final class LoanAccountData {
|
||||
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, paidInAdvance, acc.interestRatesPeriods,
|
||||
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
|
||||
product.getMaximumGapBetweenInstallments(), acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization(), acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization(), acc.rates, acc.isRatesEnabled,
|
||||
product.getFixedPrincipalPercentagePerInstallment());
|
||||
|
||||
}
|
||||
|
||||
@@ -1222,7 +1234,7 @@ public final class LoanAccountData {
|
||||
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus,
|
||||
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
|
||||
final BigDecimal topupAmount, final boolean isEqualAmortization) {
|
||||
final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
|
||||
final LoanScheduleData repaymentSchedule = null;
|
||||
final Collection<LoanTransactionData> transactions = null;
|
||||
@@ -1281,7 +1293,7 @@ public final class LoanAccountData {
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount,
|
||||
isEqualAmortization, rates, isRatesEnabled);
|
||||
isEqualAmortization, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1333,7 +1345,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup, acc.closureLoanId,
|
||||
acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, rates, isRatesEnabled);
|
||||
acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, rates, isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||
@@ -1375,7 +1388,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||
@@ -1440,7 +1454,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -1474,7 +1489,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
||||
@@ -1502,7 +1518,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||
@@ -1531,7 +1548,8 @@ public final class LoanAccountData {
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
|
||||
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled);
|
||||
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
|
||||
acc.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
private LoanAccountData(final Long id, //
|
||||
@@ -1584,7 +1602,7 @@ public final class LoanAccountData {
|
||||
final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus, final Boolean canUseForTopup,
|
||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final boolean isTopup, final Long closureLoanId,
|
||||
final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization, final List<RateData> rates,
|
||||
final Boolean isRatesEnabled) {
|
||||
final Boolean isRatesEnabled, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
@@ -1770,7 +1788,7 @@ public final class LoanAccountData {
|
||||
this.topupAmount = topupAmount;
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
this.rates = rates;
|
||||
|
||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||
}
|
||||
|
||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
|
||||
|
||||
+21
-9
@@ -415,6 +415,9 @@ public class Loan extends AbstractPersistableCustom {
|
||||
@JoinTable(name = "m_loan_rate", joinColumns = @JoinColumn(name = "loan_id"), inverseJoinColumns = @JoinColumn(name = "rate_id"))
|
||||
private List<Rate> rates;
|
||||
|
||||
@Column(name = "fixed_principal_percentage_per_installment", scale = 2, precision = 5, nullable = true)
|
||||
private BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||
@@ -422,14 +425,14 @@ public class Loan extends AbstractPersistableCustom {
|
||||
final Set<LoanCollateralManagement> collateral, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
final LoanStatus status = null;
|
||||
final Group group = null;
|
||||
final Boolean syncDisbursementWithMeeting = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential, rates);
|
||||
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
|
||||
@@ -439,13 +442,13 @@ public class Loan extends AbstractPersistableCustom {
|
||||
final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
final LoanStatus status = null;
|
||||
final Client client = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential, rates);
|
||||
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
|
||||
@@ -455,12 +458,12 @@ public class Loan extends AbstractPersistableCustom {
|
||||
final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final List<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
final BigDecimal interestRateDifferential, final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
final LoanStatus status = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential, rates);
|
||||
interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
protected Loan() {
|
||||
@@ -473,7 +476,8 @@ public class Loan extends AbstractPersistableCustom {
|
||||
final Set<LoanCharge> loanCharges, final Set<LoanCollateralManagement> collateral, final Boolean syncDisbursementWithMeeting,
|
||||
final BigDecimal fixedEmiAmount, final List<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates) {
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential, final List<Rate> rates,
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
|
||||
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
|
||||
this.loanRepaymentScheduleDetail.validateRepaymentPeriodWithGraceSettings();
|
||||
@@ -532,6 +536,7 @@ public class Loan extends AbstractPersistableCustom {
|
||||
|
||||
// rates added here
|
||||
this.rates = rates;
|
||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
// Add net get net disbursal amount from charges and principal
|
||||
this.netDisbursalAmount = this.approvedPrincipal.subtract(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
@@ -1638,6 +1643,13 @@ public class Loan extends AbstractPersistableCustom {
|
||||
this.fixedEmiAmount = null;
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
|
||||
this.fixedPrincipalPercentagePerInstallment)) {
|
||||
this.fixedPrincipalPercentagePerInstallment = command
|
||||
.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName);
|
||||
actualChanges.put(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, this.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@@ -5610,7 +5622,7 @@ public class Loan extends AbstractPersistableCustom {
|
||||
rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
|
||||
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
|
||||
scheduleGeneratorDTO.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI());
|
||||
scheduleGeneratorDTO.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI(), this.fixedPrincipalPercentagePerInstallment);
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
@@ -5892,7 +5904,7 @@ public class Loan extends AbstractPersistableCustom {
|
||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
|
||||
false);
|
||||
false, this.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+26
-13
@@ -207,6 +207,7 @@ public final class LoanApplicationTerms {
|
||||
private int extraPeriods = 0;
|
||||
private boolean isEqualAmortization;
|
||||
private Money interestTobeApproppriated;
|
||||
private final BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
@@ -229,7 +230,7 @@ public final class LoanApplicationTerms {
|
||||
BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations,
|
||||
Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, final Integer numberOfDays,
|
||||
boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
||||
final boolean isEqualAmortization) {
|
||||
final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
@@ -244,7 +245,8 @@ public final class LoanApplicationTerms {
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
||||
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false);
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -271,7 +273,7 @@ public final class LoanApplicationTerms {
|
||||
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
|
||||
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations,
|
||||
calendarHistoryDataWrapper, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, false,
|
||||
false);
|
||||
false, null);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -289,7 +291,8 @@ public final class LoanApplicationTerms {
|
||||
BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations,
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, final Integer numberOfDays,
|
||||
final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
||||
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI) {
|
||||
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI,
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -328,7 +331,7 @@ public final class LoanApplicationTerms {
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper,
|
||||
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO,
|
||||
allowCompoundingOnEod, isEqualAmortization, isFirstRepaymentDateAllowedOnHoliday,
|
||||
isInterestToBeAppropriatedEquallyWhenGreaterThanEMI);
|
||||
isInterestToBeAppropriatedEquallyWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -387,7 +390,7 @@ public final class LoanApplicationTerms {
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
|
||||
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false);
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false, null);
|
||||
|
||||
}
|
||||
|
||||
@@ -414,8 +417,8 @@ public final class LoanApplicationTerms {
|
||||
applicationTerms.calendarHistoryDataWrapper, applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||
applicationTerms.numberOfDays, applicationTerms.isSkipRepaymentOnFirstDayOfMonth, applicationTerms.holidayDetailDTO,
|
||||
applicationTerms.allowCompoundingOnEod, applicationTerms.isEqualAmortization,
|
||||
applicationTerms.isFirstRepaymentDateAllowedOnHoliday,
|
||||
applicationTerms.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI);
|
||||
applicationTerms.isFirstRepaymentDateAllowedOnHoliday, applicationTerms.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI,
|
||||
applicationTerms.fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -440,7 +443,7 @@ public final class LoanApplicationTerms {
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||
final Integer numberOfDays, final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO,
|
||||
final boolean allowCompoundingOnEod, final boolean isEqualAmortization, final boolean isFirstRepaymentDateAllowedOnHoliday,
|
||||
final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI) {
|
||||
final boolean isInterestToBeAppropriatedEquallyWhenGreaterThanEMI, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
@@ -515,6 +518,7 @@ public final class LoanApplicationTerms {
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
this.isFirstRepaymentDateAllowedOnHoliday = isFirstRepaymentDateAllowedOnHoliday;
|
||||
this.isInterestToBeAppropriatedEquallyWhenGreaterThanEMI = isInterestToBeAppropriatedEquallyWhenGreaterThanEMI;
|
||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -906,8 +910,14 @@ public final class LoanApplicationTerms {
|
||||
final int totalRepaymentsWithCapitalPayment = calculateNumberOfRepaymentsWithPrincipalPayment();
|
||||
Money principalPerPeriod = null;
|
||||
if (getFixedEmiAmount() == null) {
|
||||
principalPerPeriod = this.principal.minus(totalPrincipalAccounted)
|
||||
.dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(this.adjustPrincipalForFlatLoans);
|
||||
if (this.fixedPrincipalPercentagePerInstallment != null) {
|
||||
principalPerPeriod = this.principal.minus(totalPrincipalAccounted)
|
||||
.percentageOf(this.fixedPrincipalPercentagePerInstallment, mc.getRoundingMode())
|
||||
.plus(this.adjustPrincipalForFlatLoans);
|
||||
} else {
|
||||
principalPerPeriod = this.principal.minus(totalPrincipalAccounted)
|
||||
.dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(this.adjustPrincipalForFlatLoans);
|
||||
}
|
||||
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
||||
principalPerPeriod = principalPerPeriod.zero();
|
||||
}
|
||||
@@ -1289,8 +1299,11 @@ public final class LoanApplicationTerms {
|
||||
principal = this.principal.dividedBy(numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
|
||||
this.fixedPrincipalAmount = principal.getAmount();
|
||||
}
|
||||
principal = Money.of(getCurrency(), getFixedPrincipalAmount());
|
||||
|
||||
if (this.fixedPrincipalPercentagePerInstallment != null) {
|
||||
principal = this.principal.percentageOf(this.fixedPrincipalPercentagePerInstallment, mc.getRoundingMode());
|
||||
} else {
|
||||
principal = Money.of(getCurrency(), getFixedPrincipalAmount());
|
||||
}
|
||||
if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
|
||||
principal = principal.zero();
|
||||
}
|
||||
|
||||
+4
-1
@@ -206,6 +206,9 @@ public class LoanScheduleAssembler {
|
||||
isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isEqualAmortizationParam, element);
|
||||
}
|
||||
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, element);
|
||||
|
||||
// interest terms
|
||||
final Integer interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(interestType);
|
||||
@@ -457,7 +460,7 @@ public class LoanScheduleAssembler {
|
||||
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO,
|
||||
loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, isSkipMeetingOnFirstDay, detailDTO,
|
||||
allowCompoundingOnEod, isEqualAmortization);
|
||||
allowCompoundingOnEod, isEqualAmortization, fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
|
||||
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
LoanApiConstants.interestRateDifferentialParameterName, LoanApiConstants.repaymentFrequencyNthDayTypeParameterName,
|
||||
LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName, LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose,
|
||||
LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam, LoanProductConstants.RATES_PARAM_NAME,
|
||||
LoanApiConstants.daysInYearTypeParameterName));
|
||||
LoanApiConstants.daysInYearTypeParameterName, LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
|
||||
+28
-3
@@ -49,6 +49,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollateralQuantity;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidAmountOfCollaterals;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
@@ -92,8 +93,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
LoanApiConstants.createStandingInstructionAtDisbursementParameterName, LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose,
|
||||
LoanApiConstants.datatables, LoanApiConstants.isEqualAmortizationParam, LoanProductConstants.RATES_PARAM_NAME,
|
||||
LoanApiConstants.applicationId, // glim specific
|
||||
LoanApiConstants.lastApplication, LoanApiConstants.daysInYearTypeParameterName)); // glim
|
||||
// specific
|
||||
LoanApiConstants.lastApplication, // glim specific
|
||||
LoanApiConstants.daysInYearTypeParameterName, LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
|
||||
@@ -176,6 +177,11 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName)
|
||||
.value(fixedPrincipalPercentagePerInstallment).notLessThanMin(BigDecimal.ONE).notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
|
||||
final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
|
||||
baseDataValidator.reset().parameter("productId").value(productId).notNull().integerGreaterThanZero();
|
||||
|
||||
@@ -309,6 +315,12 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(amortizationTypeParameterName, element);
|
||||
baseDataValidator.reset().parameter(amortizationTypeParameterName).value(amortizationType).notNull().inMinMaxRange(0, 1);
|
||||
|
||||
if (!amortizationType.equals(AmortizationMethod.EQUAL_PRINCIPAL.getValue()) && fixedPrincipalPercentagePerInstallment != null) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName).failWithCode(
|
||||
"not.supported.principal.fixing.not.allowed.with.equal.installments",
|
||||
"Principal fixing cannot be done with equal installment amortization");
|
||||
}
|
||||
|
||||
final String expectedDisbursementDateParameterName = "expectedDisbursementDate";
|
||||
final LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed(expectedDisbursementDateParameterName,
|
||||
element);
|
||||
@@ -574,6 +586,11 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName)
|
||||
.value(fixedPrincipalPercentagePerInstallment).notLessThanMin(BigDecimal.ONE).notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
|
||||
final String externalIdParameterName = "externalId";
|
||||
if (this.fromApiJsonHelper.parameterExists(externalIdParameterName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
@@ -752,12 +769,20 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
}
|
||||
|
||||
final String amortizationTypeParameterName = "amortizationType";
|
||||
Integer amortizationType = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(amortizationTypeParameterName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(amortizationTypeParameterName, element);
|
||||
amortizationType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(amortizationTypeParameterName, element);
|
||||
baseDataValidator.reset().parameter(amortizationTypeParameterName).value(amortizationType).notNull().inMinMaxRange(0, 1);
|
||||
}
|
||||
|
||||
if (!Integer.valueOf(AmortizationMethod.EQUAL_PRINCIPAL.getValue()).equals(amortizationType)
|
||||
&& fixedPrincipalPercentagePerInstallment != null) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName).failWithCode(
|
||||
"not.supported.principal.fixing.not.allowed.with.equal.installments",
|
||||
"Principal fixing cannot be done with equal installment amortization");
|
||||
}
|
||||
|
||||
final String expectedDisbursementDateParameterName = "expectedDisbursementDate";
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(expectedDisbursementDateParameterName, element)) {
|
||||
|
||||
+7
-3
@@ -260,6 +260,8 @@ public class LoanAssembler {
|
||||
}
|
||||
}
|
||||
}
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, element);
|
||||
|
||||
Loan loanApplication = null;
|
||||
Client client = null;
|
||||
@@ -298,20 +300,22 @@ public class LoanAssembler {
|
||||
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(), loanProduct,
|
||||
fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, null,
|
||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates);
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
} else if (group != null) {
|
||||
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
|
||||
loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, null,
|
||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates);
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential, rates,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
} else if (client != null) {
|
||||
|
||||
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
|
||||
loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
|
||||
fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement,
|
||||
isFloatingInterestRate, interestRateDifferential, rates);
|
||||
isFloatingInterestRate, interestRateDifferential, rates, fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -610,6 +610,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " 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.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.fixed_principal_percentage_per_installment fixedPrincipalPercentagePerInstallment, "
|
||||
+ " l.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion,"
|
||||
+ " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, "
|
||||
+ " lps.name as transactionStrategyName, "
|
||||
@@ -822,6 +823,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final int interestCalculationPeriodTypeInt = JdbcSupport.getInteger(rs, "interestCalculationPeriodType");
|
||||
final boolean isEqualAmortization = rs.getBoolean("isEqualAmortization");
|
||||
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeInt);
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = rs.getBigDecimal("fixedPrincipalPercentagePerInstallment");
|
||||
final EnumOptionData interestType = LoanEnumerations.interestType(interestTypeInt);
|
||||
final EnumOptionData interestCalculationPeriodType = LoanEnumerations
|
||||
.interestCalculationPeriodType(interestCalculationPeriodTypeInt);
|
||||
@@ -997,7 +999,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
|
||||
createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, maximumGap, loanSubStatus,
|
||||
canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization);
|
||||
canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -110,6 +110,7 @@ public interface LoanProductConstants {
|
||||
// Fixed installment configuration related
|
||||
String canDefineEmiAmountParamName = "canDefineInstallmentAmount";
|
||||
String installmentAmountInMultiplesOfParamName = "installmentAmountInMultiplesOf";
|
||||
String fixedPrincipalPercentagePerInstallmentParamName = "fixedPrincipalPercentagePerInstallment";
|
||||
|
||||
// Loan Configurable Attributes
|
||||
String allowAttributeOverridesParamName = "allowAttributeOverrides";
|
||||
|
||||
+3
-2
@@ -69,6 +69,7 @@ import org.apache.fineract.portfolio.floatingrates.data.FloatingRateData;
|
||||
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.apache.fineract.portfolio.fund.data.FundData;
|
||||
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||
@@ -103,8 +104,8 @@ public class LoanProductsApiResource {
|
||||
"accountingOptions", "accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions",
|
||||
"isLinkedToFloatingInterestRates", "floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate",
|
||||
"defaultDifferentialLendingRate", "maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed",
|
||||
LoanProductConstants.CAN_USE_FOR_TOPUP, LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM,
|
||||
LoanProductConstants.RATES_PARAM_NAME));
|
||||
LoanProductConstants.CAN_USE_FOR_TOPUP, LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, LoanProductConstants.RATES_PARAM_NAME,
|
||||
LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName));
|
||||
|
||||
private final Set<String> productMixDataParameters = new HashSet<>(
|
||||
Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions"));
|
||||
|
||||
+7
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.loanproduct.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -63,6 +64,8 @@ final class LoanProductsApiResourceSwagger {
|
||||
public Integer interestRateFrequencyType;
|
||||
@Schema(example = "1")
|
||||
public Integer amortizationType;
|
||||
@Schema(example = "5.5")
|
||||
public BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
@Schema(example = "0")
|
||||
public Integer interestType;
|
||||
@Schema(example = "1")
|
||||
@@ -330,6 +333,8 @@ final class LoanProductsApiResourceSwagger {
|
||||
@Schema(example = "15.000000")
|
||||
public Float annualInterestRate;
|
||||
public GetLoanProductsAmortizationType amortizationType;
|
||||
@Schema(example = "5.5")
|
||||
public BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
public GetLoanProductsInterestType interestType;
|
||||
public GetLoansProductsInterestCalculationPeriodType interestCalculationPeriodType;
|
||||
@Schema(example = "1")
|
||||
@@ -1044,6 +1049,8 @@ final class LoanProductsApiResourceSwagger {
|
||||
@Schema(example = "60.000000")
|
||||
public Float annualInterestRate;
|
||||
public GetLoanProductsResponse.GetLoanProductsAmortizationType amortizationType;
|
||||
@Schema(example = "5.5")
|
||||
public BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
public GetLoanProductsTemplateResponse.GetLoanProductsInterestTemplateType interestType;
|
||||
public GetLoanProductsResponse.GetLoansProductsInterestCalculationPeriodType interestCalculationPeriodType;
|
||||
@Schema(example = "1")
|
||||
|
||||
+19
-5
@@ -191,6 +191,7 @@ public class LoanProductData implements Serializable {
|
||||
private LoanProductConfigurableAttributes allowAttributeOverrides;
|
||||
private final boolean syncExpectedWithDisbursementDate;
|
||||
private final boolean isEqualAmortization;
|
||||
private final BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
/**
|
||||
* Used when returning lookup information about loan product for dropdowns.
|
||||
@@ -254,6 +255,7 @@ public class LoanProductData implements Serializable {
|
||||
final LoanProductGuaranteeData productGuaranteeData = null;
|
||||
final Boolean holdGuaranteeFunds = false;
|
||||
final BigDecimal principalThresholdForLastInstallment = null;
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
|
||||
|
||||
final EnumOptionData daysInMonthType = null;
|
||||
@@ -284,7 +286,8 @@ public class LoanProductData implements Serializable {
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -355,6 +358,7 @@ public class LoanProductData implements Serializable {
|
||||
final Boolean holdGuaranteeFunds = false;
|
||||
final LoanProductGuaranteeData productGuaranteeData = null;
|
||||
final BigDecimal principalThresholdForLastInstallment = null;
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
|
||||
final boolean canDefineInstallmentAmount = false;
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
@@ -380,7 +384,8 @@ public class LoanProductData implements Serializable {
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, rates, isRatesEnabled,
|
||||
fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -458,6 +463,7 @@ public class LoanProductData implements Serializable {
|
||||
final Boolean holdGuaranteeFunds = false;
|
||||
final LoanProductGuaranteeData productGuaranteeData = null;
|
||||
final BigDecimal principalThresholdForLastInstallment = null;
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
|
||||
final boolean canDefineInstallmentAmount = false;
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
@@ -484,7 +490,7 @@ public class LoanProductData implements Serializable {
|
||||
isLinkedToFloatingInterestRates, floatingRateId, floatingRateName, interestRateDifferential, minDifferentialLendingRate,
|
||||
defaultDifferentialLendingRate, maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed,
|
||||
isVariableInstallmentsAllowed, minimumGap, maximumGap, syncExpectedWithDisbursementDate, canUseForTopup,
|
||||
isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -556,6 +562,7 @@ public class LoanProductData implements Serializable {
|
||||
final Boolean holdGuaranteeFunds = false;
|
||||
final LoanProductGuaranteeData productGuaranteeData = null;
|
||||
final BigDecimal principalThresholdForLastInstallment = null;
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = false;
|
||||
final boolean canDefineInstallmentAmount = false;
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
@@ -582,7 +589,7 @@ public class LoanProductData implements Serializable {
|
||||
isLinkedToFloatingInterestRates, floatingRateId, floatingRateName, interestRateDifferential, minDifferentialLendingRate,
|
||||
defaultDifferentialLendingRate, maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed,
|
||||
isVariableInstallmentsAllowed, minimumGap, maximumGap, syncExpectedWithDisbursementDate, canUseForTopup,
|
||||
isEqualAmortization, rateOptions, rates, isRatesEnabled);
|
||||
isEqualAmortization, rateOptions, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -624,7 +631,8 @@ public class LoanProductData implements Serializable {
|
||||
boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization,
|
||||
Collection<RateData> rateOptions, Collection<RateData> rates, final boolean isRatesEnabled) {
|
||||
Collection<RateData> rateOptions, Collection<RateData> rates, final boolean isRatesEnabled,
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -715,6 +723,7 @@ public class LoanProductData implements Serializable {
|
||||
this.holdGuaranteeFunds = holdGuaranteeFunds;
|
||||
this.productGuaranteeData = loanProductGuaranteeData;
|
||||
this.principalThresholdForLastInstallment = principalThresholdForLastInstallment;
|
||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||
this.accountMovesOutOfNPAOnlyOnArrearsCompletion = accountMovesOutOfNPAOnlyOnArrearsCompletion;
|
||||
this.allowAttributeOverrides = allowAttributeOverrides;
|
||||
|
||||
@@ -851,6 +860,7 @@ public class LoanProductData implements Serializable {
|
||||
this.holdGuaranteeFunds = productData.holdGuaranteeFunds;
|
||||
this.productGuaranteeData = productData.productGuaranteeData;
|
||||
this.principalThresholdForLastInstallment = productData.principalThresholdForLastInstallment;
|
||||
this.fixedPrincipalPercentagePerInstallment = productData.fixedPrincipalPercentagePerInstallment;
|
||||
this.accountMovesOutOfNPAOnlyOnArrearsCompletion = productData.accountMovesOutOfNPAOnlyOnArrearsCompletion;
|
||||
|
||||
this.daysInMonthTypeOptions = daysInMonthTypeOptions;
|
||||
@@ -1291,4 +1301,8 @@ public class LoanProductData implements Serializable {
|
||||
public BigDecimal getMaxInterestRatePerPeriod() {
|
||||
return maxInterestRatePerPeriod;
|
||||
}
|
||||
|
||||
public BigDecimal getFixedPrincipalPercentagePerInstallment() {
|
||||
return fixedPrincipalPercentagePerInstallment;
|
||||
}
|
||||
}
|
||||
|
||||
+20
-2
@@ -188,6 +188,9 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
@Column(name = "is_equal_amortization", nullable = false)
|
||||
private boolean isEqualAmortization = false;
|
||||
|
||||
@Column(name = "fixed_principal_percentage_per_installment", scale = 2, precision = 5, nullable = true)
|
||||
private BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate,
|
||||
final List<Rate> productRates) {
|
||||
@@ -346,6 +349,9 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
? command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM)
|
||||
: false;
|
||||
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = command
|
||||
.bigDecimalValueOfParameterNamed(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName);
|
||||
|
||||
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal,
|
||||
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType,
|
||||
annualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, repaymentEvery,
|
||||
@@ -360,7 +366,7 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
floatingRate, interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate,
|
||||
defaultDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed,
|
||||
minimumGapBetweenInstallments, maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup,
|
||||
isEqualAmortization, productRates);
|
||||
isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment);
|
||||
|
||||
}
|
||||
|
||||
@@ -595,7 +601,7 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup, final boolean isEqualAmortization,
|
||||
final List<Rate> rates) {
|
||||
final List<Rate> rates, final BigDecimal fixedPrincipalPercentagePerInstallment) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
this.name = name.trim();
|
||||
@@ -673,6 +679,7 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate;
|
||||
this.canUseForTopup = canUseForTopup;
|
||||
this.isEqualAmortization = isEqualAmortization;
|
||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
if (rates != null) {
|
||||
this.rates = rates;
|
||||
@@ -1093,6 +1100,14 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName,
|
||||
this.fixedPrincipalPercentagePerInstallment)) {
|
||||
BigDecimal newValue = command
|
||||
.bigDecimalValueOfParameterNamed(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName);
|
||||
actualChanges.put(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName, newValue);
|
||||
this.fixedPrincipalPercentagePerInstallment = newValue;
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@@ -1443,4 +1458,7 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
this.rates = rates;
|
||||
}
|
||||
|
||||
public BigDecimal getFixedPrincipalPercentagePerInstallment() {
|
||||
return fixedPrincipalPercentagePerInstallment;
|
||||
}
|
||||
}
|
||||
|
||||
+30
-4
@@ -42,7 +42,9 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
@@ -106,8 +108,8 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.recalculationRestFrequencyWeekdayParamName, LoanProductConstants.recalculationRestFrequencyNthDayParamName,
|
||||
LoanProductConstants.recalculationRestFrequencyOnDayParamName,
|
||||
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName,
|
||||
LoanProductConstants.CAN_USE_FOR_TOPUP, LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM,
|
||||
LoanProductConstants.RATES_PARAM_NAME));
|
||||
LoanProductConstants.CAN_USE_FOR_TOPUP, LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, LoanProductConstants.RATES_PARAM_NAME,
|
||||
LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName));
|
||||
|
||||
private static final String[] supportedloanConfigurableAttributes = { LoanProductConstants.amortizationTypeParamName,
|
||||
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyIdParamName,
|
||||
@@ -515,6 +517,18 @@ public final class LoanProductDataValidator {
|
||||
.extractBigDecimalWithLocaleNamed(LoanProductConstants.principalThresholdForLastInstallmentParamName, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.principalThresholdForLastInstallmentParamName)
|
||||
.value(principalThresholdForLastInstallment).notLessThanMin(BigDecimal.ZERO).notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName)
|
||||
.value(fixedPrincipalPercentagePerInstallment).notLessThanMin(BigDecimal.ONE).notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
|
||||
if (!amortizationType.equals(AmortizationMethod.EQUAL_PRINCIPAL.getValue()) && fixedPrincipalPercentagePerInstallment != null) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName).failWithCode(
|
||||
"not.supported.principal.fixing.not.allowed.with.equal.installments",
|
||||
"Principal fixing cannot be done with equal installment amortization");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.canDefineEmiAmountParamName, element)) {
|
||||
final Boolean canDefineInstallmentAmount = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(LoanProductConstants.canDefineEmiAmountParamName, element);
|
||||
@@ -1063,9 +1077,9 @@ public final class LoanProductDataValidator {
|
||||
.integerZeroOrGreater();
|
||||
}
|
||||
|
||||
//
|
||||
Integer amortizationType = null;
|
||||
if (this.fromApiJsonHelper.parameterExists("amortizationType", element)) {
|
||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
|
||||
amortizationType = this.fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1);
|
||||
}
|
||||
|
||||
@@ -1340,6 +1354,18 @@ public final class LoanProductDataValidator {
|
||||
.value(principalThresholdForLastInstallment).notNull().notLessThanMin(BigDecimal.ZERO)
|
||||
.notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
}
|
||||
|
||||
BigDecimal fixedPrincipalPercentagePerInstallment = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.fixedPrincipalPercentagePerInstallmentParamName)
|
||||
.value(fixedPrincipalPercentagePerInstallment).notLessThanMin(BigDecimal.ONE).notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
|
||||
if (!AmortizationMethod.EQUAL_PRINCIPAL.getValue().equals(amortizationType) && fixedPrincipalPercentagePerInstallment != null) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName).failWithCode(
|
||||
"not.supported.principal.fixing.not.allowed.with.equal.installments",
|
||||
"Principal fixing cannot be done with equal installment amortization");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.canDefineEmiAmountParamName, element)) {
|
||||
final Boolean canDefineInstallmentAmount = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(LoanProductConstants.canDefineEmiAmountParamName, element);
|
||||
|
||||
+3
-1
@@ -214,6 +214,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lpr.is_compounding_to_be_posted_as_transaction as isCompoundingToBePostedAsTransaction, "
|
||||
+ "lpr.allow_compounding_on_eod as allowCompoundingOnEod, " + "lp.hold_guarantee_funds as holdGuaranteeFunds, "
|
||||
+ "lp.principal_threshold_for_last_installment as principalThresholdForLastInstallment, "
|
||||
+ "lp.fixed_principal_percentage_per_installment fixedPrincipalPercentagePerInstallment, "
|
||||
+ "lp.sync_expected_with_disbursement_date as syncExpectedWithDisbursementDate, "
|
||||
+ "lpg.id as lpgId, lpg.mandatory_guarantee as mandatoryGuarantee, "
|
||||
+ "lpg.minimum_guarantee_from_own_funds as minimumGuaranteeFromOwnFunds, lpg.minimum_guarantee_from_guarantor_funds as minimumGuaranteeFromGuarantor, "
|
||||
@@ -448,6 +449,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
}
|
||||
|
||||
final BigDecimal principalThresholdForLastInstallment = rs.getBigDecimal("principalThresholdForLastInstallment");
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment = rs.getBigDecimal("fixedPrincipalPercentagePerInstallment");
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = rs.getBoolean("accountMovesOutOfNPAOnlyOnArrearsCompletion");
|
||||
final boolean syncExpectedWithDisbursementDate = rs.getBoolean("syncExpectedWithDisbursementDate");
|
||||
|
||||
@@ -471,7 +473,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
|
||||
maximumGap, syncExpectedWithDisbursementDate, canUseForTopup, isEqualAmortization, rateOptions, this.rates,
|
||||
isRatesEnabled);
|
||||
isRatesEnabled, fixedPrincipalPercentagePerInstallment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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 fixed_principal_percentage_per_installment decimal(5,2) DEFAULT NULL;
|
||||
alter table m_loan add column fixed_principal_percentage_per_installment decimal(5,2) DEFAULT NULL;
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
/**
|
||||
* 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.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public class LoanFixedPrincipalPercentageAmortizationTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LoanFixedPrincipalPercentageAmortizationTest.class);
|
||||
|
||||
private static final String ACCOUNTING_NONE = "1";
|
||||
|
||||
private ResponseSpecification responseSpec;
|
||||
private RequestSpecification requestSpec;
|
||||
private LoanTransactionHelper loanTransactionHelper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
Utils.initializeRESTAssured();
|
||||
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
// this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||
// this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkLoanCreateAndDisburseFlowWithFixedPrincipalPercentage() {
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||
final Integer loanProductID = createLoanProduct(ACCOUNTING_NONE);
|
||||
final Integer loanID = applyForLoanApplication(clientID, loanProductID, null, null, "100000.00");
|
||||
final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec,
|
||||
loanID);
|
||||
verifyLoanRepaymentScheduleForEqualPrincipal(loanSchedule);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkLoanCreateAndDisburseFlowWithFixedPrincipalPercentageWithPrincipalGrace() {
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||
final Integer loanProductID = createLoanProduct(ACCOUNTING_NONE);
|
||||
final Integer loanID = applyForLoanApplicationWithPrincipalGrace(clientID, loanProductID, null, null, "100000.00");
|
||||
final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec,
|
||||
loanID);
|
||||
verifyLoanRepaymentScheduleForEqualPrincipalWithPrincipalGrace(loanSchedule);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkLoanCreateAndDisburseFlowWithFixedPrincipalPercentageAndFlatInterest() {
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||
final Integer loanProductID = createLoanProductWithFlatInterest(ACCOUNTING_NONE);
|
||||
final Integer loanID = applyForLoanApplicationWithFlatInterest(clientID, loanProductID, null, null, "100000.00");
|
||||
final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec,
|
||||
loanID);
|
||||
verifyLoanRepaymentScheduleForEqualPrincipalAndFlatInterest(loanSchedule);
|
||||
}
|
||||
|
||||
private Integer createLoanProduct(final String accountingRule, final Account... accounts) {
|
||||
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
LoanProductTestBuilder builder = new LoanProductTestBuilder() //
|
||||
.withPrincipal("100000.00") //
|
||||
.withNumberOfRepayments("13") //
|
||||
.withRepaymentAfterEvery("1") //
|
||||
.withRepaymentTypeAsMonth() //
|
||||
.withinterestRatePerPeriod("1") //
|
||||
.withInterestCalculationPeriodTypeAsDays().withInterestRateFrequencyTypeAsMonths() //
|
||||
.withAmortizationTypeAsEqualPrincipalPayment() // This is required to fix the principal
|
||||
.withPrinciplePercentagePerInstallment("5.00") // This fixes the principal at a fixed value till the
|
||||
// second last EMI
|
||||
.withInterestTypeAsDecliningBalance() //
|
||||
.withAccounting(accountingRule, accounts);
|
||||
|
||||
final String loanProductJSON = builder.build(null);
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, List<HashMap> charges,
|
||||
final String savingsId, String principal) {
|
||||
|
||||
List<HashMap> collaterals = new ArrayList<>();
|
||||
|
||||
final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
|
||||
Assertions.assertNotNull(collateralId);
|
||||
final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
|
||||
String.valueOf(clientID), collateralId);
|
||||
Assertions.assertNotNull(clientCollateralId);
|
||||
addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
|
||||
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||
.withPrincipal(principal) //
|
||||
.withLoanTermFrequency("13") //
|
||||
.withLoanTermFrequencyAsMonths() //
|
||||
.withNumberOfRepayments("13") //
|
||||
.withRepaymentEveryAfter("1") //
|
||||
.withRepaymentFrequencyTypeAsMonths() //
|
||||
.withInterestRatePerPeriod("2") //
|
||||
.withAmortizationTypeAsEqualInstallments() //
|
||||
.withAmortizationTypeAsEqualPrincipalPayments() // This is required to fix the principal
|
||||
.withPrinciplePercentagePerInstallment("5.00") // This fixes the principal at a fixed value till the
|
||||
// second last EMI
|
||||
.withInterestTypeAsDecliningBalance() //
|
||||
.withInterestCalculationPeriodTypeAsDays() //
|
||||
.withExpectedDisbursementDate("20 September 2011") //
|
||||
.withSubmittedOnDate("20 September 2011") //
|
||||
.withCollaterals(collaterals).withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
|
||||
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
}
|
||||
|
||||
private void addCollaterals(List<HashMap> collaterals, Integer collateralId, BigDecimal quantity) {
|
||||
collaterals.add(collaterals(collateralId, quantity));
|
||||
}
|
||||
|
||||
private HashMap<String, String> collaterals(Integer collateralId, BigDecimal quantity) {
|
||||
HashMap<String, String> collateral = new HashMap<String, String>(2);
|
||||
collateral.put("clientCollateralId", collateralId.toString());
|
||||
collateral.put("quantity", quantity.toString());
|
||||
return collateral;
|
||||
}
|
||||
|
||||
private Integer applyForLoanApplicationWithPrincipalGrace(final Integer clientID, final Integer loanProductID, List<HashMap> charges,
|
||||
final String savingsId, String principal) {
|
||||
List<HashMap> collaterals = new ArrayList<>();
|
||||
|
||||
final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
|
||||
Assertions.assertNotNull(collateralId);
|
||||
final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
|
||||
String.valueOf(clientID), collateralId);
|
||||
Assertions.assertNotNull(clientCollateralId);
|
||||
addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||
.withPrincipal(principal) //
|
||||
.withLoanTermFrequency("19") //
|
||||
.withLoanTermFrequencyAsMonths() //
|
||||
.withNumberOfRepayments("19") //
|
||||
.withRepaymentEveryAfter("1") //
|
||||
.withRepaymentFrequencyTypeAsMonths() //
|
||||
.withInterestRatePerPeriod("2") //
|
||||
.withAmortizationTypeAsEqualInstallments() //
|
||||
.withAmortizationTypeAsEqualPrincipalPayments() // This is required to fix the principal
|
||||
.withPrinciplePercentagePerInstallment("5.00") // This fixes the principal at a fixed value till the
|
||||
// second last EMI
|
||||
.withPrincipalGrace("6").withInterestTypeAsDecliningBalance() //
|
||||
.withInterestCalculationPeriodTypeAsDays() //
|
||||
.withExpectedDisbursementDate("20 September 2011") //
|
||||
.withSubmittedOnDate("20 September 2011") //
|
||||
.withCollaterals(collaterals).withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
|
||||
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
}
|
||||
|
||||
private void verifyLoanRepaymentScheduleForEqualPrincipal(final ArrayList<HashMap> loanSchedule) {
|
||||
LOG.info("--------------------VERIFYING THE PRINCIPAL DUES,INTEREST DUE AND DUE DATE--------------------------");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2011, 10, 20)), loanSchedule.get(1).get("dueDate"),
|
||||
"Checking for Due Date for 1st Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(1).get("principalOriginalDue"), "Checking for Principal Due for 1st Month");
|
||||
assertEquals(Float.parseFloat("1972.60"), loanSchedule.get(1).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 1st Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2011, 11, 20)), loanSchedule.get(2).get("dueDate"),
|
||||
"Checking for Due Date for 2nd Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(2).get("principalDue"), "Checking for Principal Due for 2nd Month");
|
||||
assertEquals(Float.parseFloat("1936.44"), loanSchedule.get(2).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 2nd Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2011, 12, 20)), loanSchedule.get(3).get("dueDate"),
|
||||
"Checking for Due Date for 3rd Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(3).get("principalDue"), "Checking for Principal Due for 3rd Month");
|
||||
assertEquals(Float.parseFloat("1775.34"), loanSchedule.get(3).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 3rd Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2012, 9, 20)), loanSchedule.get(12).get("dueDate"),
|
||||
"Checking for Due Date for 12th Month");
|
||||
assertEquals(Float.parseFloat("5000 "), loanSchedule.get(12).get("principalDue"), "Checking for Principal Due for 12th Month");
|
||||
assertEquals(Float.parseFloat("917.26"), loanSchedule.get(12).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 12th Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2012, 10, 20)), loanSchedule.get(13).get("dueDate"),
|
||||
"Checking for Due Date for 13th Month - Last EMI");
|
||||
assertEquals(Float.parseFloat("40000"), loanSchedule.get(13).get("principalDue"),
|
||||
"Checking for Principal Due for 13th Month - Last EMI");
|
||||
assertEquals(Float.parseFloat("789.04"), loanSchedule.get(13).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 13th Month - Last EMI");
|
||||
|
||||
}
|
||||
|
||||
private void verifyLoanRepaymentScheduleForEqualPrincipalWithPrincipalGrace(final ArrayList<HashMap> loanSchedule) {
|
||||
LOG.info("--------------------VERIFYING THE PRINCIPAL DUES,INTEREST DUE AND DUE DATE--------------------------");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2011, 10, 20)), loanSchedule.get(1).get("dueDate"),
|
||||
"Checking for Due Date for 1st Month");
|
||||
assertEquals(Integer.parseInt("0"), loanSchedule.get(1).get("principalOriginalDue"), "Checking for Principal Due for 1st Month");
|
||||
assertEquals(Float.parseFloat("1972.6"), loanSchedule.get(1).get("interestOriginalDue"), "Checking for Interest Due for 1st Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2012, 3, 20)), loanSchedule.get(6).get("dueDate"),
|
||||
"Checking for Due Date for 6th Month");
|
||||
assertEquals(Integer.parseInt("0"), loanSchedule.get(6).get("principalDue"), "Checking for Principal Due for 6th Month");
|
||||
assertEquals(Float.parseFloat("1906.85"), loanSchedule.get(6).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 6th Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2012, 4, 20)), loanSchedule.get(7).get("dueDate"),
|
||||
"Checking for Due Date for 7th Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(7).get("principalDue"), "Checking for Principal Due for 7th Month");
|
||||
assertEquals(Float.parseFloat("2038.36"), loanSchedule.get(7).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 7th Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2013, 3, 20)), loanSchedule.get(18).get("dueDate"),
|
||||
"Checking for Due Date for 18th Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(18).get("principalDue"), "Checking for Principal Due for 18th Month");
|
||||
assertEquals(Float.parseFloat("828.49"), loanSchedule.get(18).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 18th Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2013, 4, 20)), loanSchedule.get(19).get("dueDate"),
|
||||
"Checking for Due Date for 19th Month - Last EMI");
|
||||
assertEquals(Float.parseFloat("40000"), loanSchedule.get(19).get("principalDue"),
|
||||
"Checking for Principal Due for 19th Month - Last EMI");
|
||||
assertEquals(Float.parseFloat("815.34"), loanSchedule.get(19).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 19th Month - Last EMI");
|
||||
|
||||
}
|
||||
|
||||
private Integer createLoanProductWithFlatInterest(final String accountingRule, final Account... accounts) {
|
||||
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
LoanProductTestBuilder builder = new LoanProductTestBuilder() //
|
||||
.withPrincipal("100000.00") //
|
||||
.withNumberOfRepayments("13") //
|
||||
.withRepaymentAfterEvery("1") //
|
||||
.withRepaymentTypeAsMonth() //
|
||||
.withinterestRatePerPeriod("1") //
|
||||
.withInterestCalculationPeriodTypeAsDays().withInterestRateFrequencyTypeAsMonths() //
|
||||
.withAmortizationTypeAsEqualPrincipalPayment() // This is required to fix the principal
|
||||
.withPrinciplePercentagePerInstallment("5.00") // This fixes the principal at a fixed value till the
|
||||
// second last EMI
|
||||
.withInterestTypeAsFlat() //
|
||||
.withAccounting(accountingRule, accounts);
|
||||
|
||||
final String loanProductJSON = builder.build(null);
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private Integer applyForLoanApplicationWithFlatInterest(final Integer clientID, final Integer loanProductID, List<HashMap> charges,
|
||||
final String savingsId, String principal) {
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||
.withPrincipal(principal) //
|
||||
.withLoanTermFrequency("13") //
|
||||
.withLoanTermFrequencyAsMonths() //
|
||||
.withNumberOfRepayments("13") //
|
||||
.withRepaymentEveryAfter("1") //
|
||||
.withRepaymentFrequencyTypeAsMonths() //
|
||||
.withInterestRatePerPeriod("2") //
|
||||
.withAmortizationTypeAsEqualInstallments() //
|
||||
.withAmortizationTypeAsEqualPrincipalPayments() // This is required to fix the principal
|
||||
.withPrinciplePercentagePerInstallment("5.00") // This fixes the principal at a fixed value till the
|
||||
// second last EMI
|
||||
.withInterestTypeAsFlatBalance() //
|
||||
.withInterestCalculationPeriodTypeAsDays() //
|
||||
.withExpectedDisbursementDate("20 September 2011") //
|
||||
.withSubmittedOnDate("20 September 2011") //
|
||||
.withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
|
||||
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
}
|
||||
|
||||
private void verifyLoanRepaymentScheduleForEqualPrincipalAndFlatInterest(final ArrayList<HashMap> loanSchedule) {
|
||||
LOG.info("--------------------VERIFYING THE PRINCIPAL DUES,INTEREST DUE AND DUE DATE--------------------------");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2011, 10, 20)), loanSchedule.get(1).get("dueDate"),
|
||||
"Checking for Due Date for 1st Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(1).get("principalOriginalDue"), "Checking for Principal Due for 1st Month");
|
||||
assertEquals(Float.parseFloat("2002.95"), loanSchedule.get(1).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 1st Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2011, 11, 20)), loanSchedule.get(2).get("dueDate"),
|
||||
"Checking for Due Date for 2nd Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(2).get("principalDue"), "Checking for Principal Due for 2nd Month");
|
||||
assertEquals(Float.parseFloat("2002.95"), loanSchedule.get(2).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 2nd Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2011, 12, 20)), loanSchedule.get(3).get("dueDate"),
|
||||
"Checking for Due Date for 3rd Month");
|
||||
assertEquals(Float.parseFloat("5000"), loanSchedule.get(3).get("principalDue"), "Checking for Principal Due for 3rd Month");
|
||||
assertEquals(Float.parseFloat("2002.95"), loanSchedule.get(3).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 3rd Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2012, 9, 20)), loanSchedule.get(12).get("dueDate"),
|
||||
"Checking for Due Date for 12th Month");
|
||||
assertEquals(Float.parseFloat("5000 "), loanSchedule.get(12).get("principalDue"), "Checking for Principal Due for 12th Month");
|
||||
assertEquals(Float.parseFloat("2002.95"), loanSchedule.get(12).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 12th Month");
|
||||
|
||||
assertEquals(new ArrayList<>(Arrays.asList(2012, 10, 20)), loanSchedule.get(13).get("dueDate"),
|
||||
"Checking for Due Date for 13th Month - Last EMI");
|
||||
assertEquals(Float.parseFloat("40000"), loanSchedule.get(13).get("principalDue"),
|
||||
"Checking for Principal Due for 13th Month - Last EMI");
|
||||
assertEquals(Float.parseFloat("2002.96"), loanSchedule.get(13).get("interestOriginalDue"),
|
||||
"Checking for Interest Due for 13th Month - Last EMI");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -76,6 +76,7 @@ public class LoanApplicationTestBuilder {
|
||||
private boolean syncDisbursementWithMeeting = false;
|
||||
private List<HashMap<String, Object>> datatables = null;
|
||||
private List<Map<String, Object>> approvalFormData = null;
|
||||
private String fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
public String build(final String clientID, final String groupID, final String loanProductId, final String savingsID) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
@@ -141,6 +142,7 @@ public class LoanApplicationTestBuilder {
|
||||
map.put("repaymentFrequencyType", this.repaymentFrequencyType);
|
||||
map.put("interestRatePerPeriod", this.interestRate);
|
||||
map.put("amortizationType", this.amortizationType);
|
||||
map.put("fixedPrincipalPercentagePerInstallment", fixedPrincipalPercentagePerInstallment);
|
||||
map.put("interestType", this.interestType);
|
||||
map.put("interestCalculationPeriodType", this.interestCalculationPeriodType);
|
||||
map.put("transactionProcessingStrategyId", this.transactionProcessingID);
|
||||
@@ -375,4 +377,9 @@ public class LoanApplicationTestBuilder {
|
||||
this.datatables = datatables;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTestBuilder withPrinciplePercentagePerInstallment(String fixedPrincipalPercentagePerInstallment) {
|
||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -124,6 +124,7 @@ public class LoanProductTestBuilder {
|
||||
private Integer recalculationCompoundingFrequencyDayOfWeekType = null;
|
||||
private Integer recalculationRestFrequencyDayOfWeekType = null;
|
||||
private boolean syncExpectedWithDisbursementDate = false;
|
||||
private String fixedPrincipalPercentagePerInstallment;
|
||||
|
||||
public String build(final String chargeId) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
@@ -149,6 +150,7 @@ public class LoanProductTestBuilder {
|
||||
map.put("interestRatePerPeriod", this.interestRatePerPeriod);
|
||||
map.put("interestRateFrequencyType", this.interestRateFrequencyType);
|
||||
map.put("amortizationType", this.amortizationType);
|
||||
map.put("fixedPrincipalPercentagePerInstallment", fixedPrincipalPercentagePerInstallment);
|
||||
map.put("interestType", this.interestType);
|
||||
map.put("interestCalculationPeriodType", this.interestCalculationPeriodType);
|
||||
map.put("inArrearsTolerance", this.inArrearsTolerance);
|
||||
@@ -506,4 +508,9 @@ public class LoanProductTestBuilder {
|
||||
this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withPrinciplePercentagePerInstallment(String fixedPrincipalPercentagePerInstallment) {
|
||||
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user