diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index a0dd71e7a..3df620a16 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -11032,6 +11032,12 @@ Request Body:
| Override the default value for rounding currency to multiples of value provided. |
+
+ | instalmentAmountInMultiplesOf |
+
+
+ | Override the default value for rounding instalment amount to multiples of value provided. |
+
| principal |
@@ -11133,6 +11139,13 @@ Request Body:
The minimum number of days allowed between a Loan disbursal and its first repayment.
|
+
+ | canDefineInstalmentAmount |
+
+
+ | if provided as true, then fixed instalment amount can be provided from loan account.
+ |
+
@@ -12499,7 +12512,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
numberOfRepaymentVariationsForBorrowerCycle,
interestRateVariationsForBorrowerCycle,
multiDisburseLoan,maxTrancheCount,
- outstandingLoanBalance,overdueDaysForNPA,holdGuaranteeFunds
+ outstandingLoanBalance,overdueDaysForNPA, holdGuaranteeFunds, canDefineInstalmentAmount, instalmentAmountInMultiplesOf
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/AccountNumberPreferencesTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/AccountNumberPreferencesTest.java
index 62eb3d050..3b0c245bd 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/AccountNumberPreferencesTest.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/AccountNumberPreferencesTest.java
@@ -1,3 +1,8 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
package org.mifosplatform.integrationtests;
import com.jayway.restassured.builder.RequestSpecBuilder;
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesHelper.java
index 9745f7219..02f0ec059 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesHelper.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesHelper.java
@@ -1,3 +1,8 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
package org.mifosplatform.integrationtests.common.system;
import java.util.ArrayList;
diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesTestBuilder.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesTestBuilder.java
index 6488c36c0..7a6b088e1 100644
--- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesTestBuilder.java
+++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/system/AccountNumberPreferencesTestBuilder.java
@@ -1,3 +1,8 @@
+/**
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
package org.mifosplatform.integrationtests.common.system;
import java.util.HashMap;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java
index e9e300185..076201f05 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java
@@ -74,21 +74,27 @@ public class Money implements Comparable {
// round monetary amounts into multiplesof say 20/50.
if (inMultiplesOf != null && this.currencyDigitsAfterDecimal == 0 && inMultiplesOf > 0 && amountScaled.doubleValue() > 0) {
final double existingVal = amountScaled.doubleValue();
- final double ceilingOfValue = ceiling(existingVal, inMultiplesOf);
- final double floorOfValue = floor(existingVal, inMultiplesOf);
-
- final double floorDiff = existingVal - floorOfValue;
- final double ceilDiff = ceilingOfValue - existingVal;
-
- if (ceilDiff > floorDiff) {
- amountScaled = BigDecimal.valueOf(floorOfValue);
- } else {
- amountScaled = BigDecimal.valueOf(ceilingOfValue);
- }
+ amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, inMultiplesOf));
}
this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN);
}
+ public static double roundToMultiplesOf(final double existingVal, final Integer inMultiplesOf) {
+ double amountScaled = existingVal;
+ final double ceilingOfValue = ceiling(existingVal, inMultiplesOf);
+ final double floorOfValue = floor(existingVal, inMultiplesOf);
+
+ final double floorDiff = existingVal - floorOfValue;
+ final double ceilDiff = ceilingOfValue - existingVal;
+
+ if (ceilDiff > floorDiff) {
+ amountScaled = floorOfValue;
+ } else {
+ amountScaled = ceilingOfValue;
+ }
+ return amountScaled;
+ }
+
public static double ceiling(final double n, final double s) {
double c;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
index 34fa39785..e9cda5bf1 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
@@ -152,6 +152,8 @@ public class LoanAccountData {
private final Boolean multiDisburseLoan;
+ private final Boolean canDefineInstalmentAmount;
+
private final BigDecimal fixedEmiAmount;
private final BigDecimal maxOutstandingLoanBalance;
@@ -260,6 +262,7 @@ public class LoanAccountData {
final PortfolioAccountData linkedAccount = null;
final Collection disbursementData = null;
final Boolean multiDisburseLoan = null;
+ final Boolean canDefineInstalmentAmount = null;
final BigDecimal fixedEmiAmount = null;
final BigDecimal maxOutstandingLoanBalance = null;
final Collection emiAmountVariations = null;
@@ -289,9 +292,10 @@ public class LoanAccountData {
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
- disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
- product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
- isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
+ disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
+ emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
+ daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
+ createStandingInstructionAtDisbursement);
}
/**
@@ -377,6 +381,7 @@ public class LoanAccountData {
final PortfolioAccountData linkedAccount = null;
final Collection disbursementData = null;
final Boolean multiDisburseLoan = null;
+ final Boolean canDefineInstalmentAmount = null;
final BigDecimal fixedEmiAmount = null;
final BigDecimal maxOutstandingLoanBalance = null;
final Collection emiAmountVariations = null;
@@ -406,9 +411,10 @@ public class LoanAccountData {
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
- disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
- product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
- isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
+ disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
+ emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
+ daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
+ createStandingInstructionAtDisbursement);
}
@@ -430,10 +436,11 @@ public class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
- acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
- acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
- acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
- acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
+ acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
+ acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
+ acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
+ acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
+ acc.createStandingInstructionAtDisbursement);
}
/**
@@ -520,6 +527,7 @@ public class LoanAccountData {
final PortfolioAccountData linkedAccount = null;
final Collection disbursementData = null;
final Boolean multiDisburseLoan = null;
+ final Boolean canDefineInstalmentAmount = null;
final BigDecimal fixedEmiAmount = null;
final BigDecimal maxOutstandingBalance = null;
final Collection emiAmountVariations = null;
@@ -549,9 +557,9 @@ public class LoanAccountData {
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
- disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance, emiAmountVariations, memberVariations, product,
- inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
- interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
+ disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmount, maxOutstandingBalance, emiAmountVariations,
+ memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
+ isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
}
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
@@ -572,10 +580,11 @@ public class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
- acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
- acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
- acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
- acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
+ acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
+ acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
+ acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
+ acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
+ acc.createStandingInstructionAtDisbursement);
}
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
@@ -707,10 +716,11 @@ public class LoanAccountData {
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
- disbursementData, product.getMultiDisburseLoan(), fixedEmi, product.getOutstandingLoanBalance(), emiAmountVariations,
- memberVariations, product, inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA,
- product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
- product.toLoanInterestRecalculationData(), originalSchedule, createStandingInstructionAtDisbursement);
+ disbursementData, product.getMultiDisburseLoan(), product.canDefineInstalmentAmount(), fixedEmi,
+ product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
+ product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(),
+ product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
+ originalSchedule, createStandingInstructionAtDisbursement);
}
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -760,11 +770,11 @@ public class LoanAccountData {
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes,
- acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount,
- product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears,
- product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(),
- product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
- acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
+ acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(),
+ product.canDefineInstalmentAmount(), acc.fixedEmiAmount, product.getOutstandingLoanBalance(), acc.emiAmountVariations,
+ acc.memberVariations, product, acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA,
+ product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
+ product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
}
/*
@@ -786,9 +796,9 @@ public class LoanAccountData {
final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline,
final LoanSummaryData loanSummary, final BigDecimal feeChargesDueAtDisbursementCharged,
final Boolean syncDisbursementWithMeeting, final Integer loancounter, final Integer loanProductCounter,
- final Boolean multiDisburseLoan, final BigDecimal fixedEmiAmont, final BigDecimal outstandingLoanBalance,
- final Boolean inArrears, final Integer graceOnArrearsAgeing, final Boolean isNPA, final EnumOptionData daysInMonthType,
- final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
+ final Boolean multiDisburseLoan, Boolean canDefineInstalmentAmount, final BigDecimal fixedEmiAmont,
+ final BigDecimal outstandingLoanBalance, final Boolean inArrears, final Integer graceOnArrearsAgeing, final Boolean isNPA,
+ final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement) {
final LoanScheduleData repaymentSchedule = null;
@@ -837,9 +847,9 @@ public class LoanAccountData {
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
- disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance, emiAmountVariations, memberVariations, product,
- inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
- interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
+ disbursementData, multiDisburseLoan, canDefineInstalmentAmount, fixedEmiAmont, outstandingLoanBalance, emiAmountVariations,
+ memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
+ isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement);
}
/*
@@ -878,9 +888,10 @@ public class LoanAccountData {
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, notes, accountLinkingOptions, linkedAccount,
- disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, emiAmountVariations,
- acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType,
- acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
+ disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
+ acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
+ acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
+ acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement);
}
@@ -913,10 +924,11 @@ public class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
- acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
- acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
- acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
- acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
+ acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
+ acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
+ acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
+ acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
+ acc.createStandingInstructionAtDisbursement);
}
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map memberLoanCycle) {
@@ -973,10 +985,11 @@ public class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
- acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
- acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
- acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
- acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
+ acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
+ acc.maxOutstandingLoanBalance, acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears,
+ acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
+ acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
+ acc.createStandingInstructionAtDisbursement);
}
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
@@ -999,10 +1012,11 @@ public class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
- acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
- acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
- acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData,
- acc.originalSchedule, acc.createStandingInstructionAtDisbursement);
+ acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
+ acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
+ acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
+ acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
+ acc.createStandingInstructionAtDisbursement);
}
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
@@ -1023,10 +1037,11 @@ public class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
- acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
- acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
- acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
- originalSchedule, acc.createStandingInstructionAtDisbursement);
+ acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.canDefineInstalmentAmount, acc.fixedEmiAmount,
+ acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
+ acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
+ acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
+ acc.createStandingInstructionAtDisbursement);
}
private LoanAccountData(
@@ -1081,11 +1096,11 @@ public class LoanAccountData {
final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter,
final Collection notes, final Collection accountLinkingOptions,
final PortfolioAccountData linkedAccount, final Collection disbursementDetails,
- final Boolean multiDisburseLoan, BigDecimal fixedEmiAmount, final BigDecimal maxOutstandingLoanBalance,
- final Collection emiAmountVariations, final Map memberVariations,
- final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing,
- final Collection overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType,
- final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
+ final Boolean multiDisburseLoan, final Boolean canDefineInstalmentAmount, BigDecimal fixedEmiAmount,
+ final BigDecimal maxOutstandingLoanBalance, final Collection emiAmountVariations,
+ final Map memberVariations, final LoanProductData product, final Boolean inArrears,
+ final Integer graceOnArrearsAgeing, final Collection overdueCharges, final Boolean isNPA,
+ final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
final Boolean createStandingInstructionAtDisbursement) {
@@ -1209,6 +1224,14 @@ public class LoanAccountData {
this.accountLinkingOptions = accountLinkingOptions;
this.disbursementDetails = disbursementDetails;
this.multiDisburseLoan = multiDisburseLoan;
+
+ Boolean canDefineEMIAmount = canDefineInstalmentAmount;
+ if (canDefineInstalmentAmount == null) {
+ canDefineEMIAmount = multiDisburseLoan;
+ } else if (multiDisburseLoan != null) {
+ canDefineEMIAmount = canDefineInstalmentAmount || multiDisburseLoan;
+ }
+ this.canDefineInstalmentAmount = canDefineEMIAmount;
this.fixedEmiAmount = fixedEmiAmount;
this.maxOutstandingLoanBalance = maxOutstandingLoanBalance;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
index e79e54826..d2e64a6d5 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
@@ -1195,9 +1195,9 @@ public class Loan extends AbstractPersistable {
if (command.isChangeInBooleanParameterNamed(createSiAtDisbursementParameterName, shouldCreateStandingInstructionAtDisbursement())) {
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(createSiAtDisbursementParameterName);
actualChanges.put(createSiAtDisbursementParameterName, valueAsInput);
- this.createStandingInstructionAtDisbursement = valueAsInput;
+ this.createStandingInstructionAtDisbursement = valueAsInput;
}
-
+
final String externalIdParamName = "externalId";
if (command.isChangeInStringParameterNamed(externalIdParamName, this.externalId)) {
final String newValue = command.stringValueOfParameterNamed(externalIdParamName);
@@ -1398,9 +1398,6 @@ public class Loan extends AbstractPersistable {
if (loanProduct.isMultiDisburseLoan()) {
updateDisbursementDetails(command, actualChanges);
- if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) {
- this.fixedEmiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
- }
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.maxOutstandingBalanceParameterName,
this.maxOutstandingLoanBalance)) {
this.maxOutstandingLoanBalance = command
@@ -1417,6 +1414,15 @@ public class Loan extends AbstractPersistable {
}
} else {
this.disbursementDetails.clear();
+ }
+
+ if (loanProduct.isMultiDisburseLoan() || loanProduct.canDefineInstalmentAmount()) {
+ if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount)) {
+ this.fixedEmiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
+ actualChanges.put(LoanApiConstants.emiAmountParameterName, this.fixedEmiAmount);
+ actualChanges.put("recalculateLoanSchedule", true);
+ }
+ } else {
this.fixedEmiAmount = null;
}
@@ -2076,10 +2082,15 @@ public class Loan extends AbstractPersistable {
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser) {
boolean isEmiAmountChanged = false;
- if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
- LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
- actualDisbursementDate.toDate(), emiAmount, this);
- this.loanTermVariations.add(loanVariationTerms);
+ if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstalmentAmount()) && emiAmount != null
+ && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
+ if (this.loanProduct.isMultiDisburseLoan()) {
+ LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
+ actualDisbursementDate.toDate(), emiAmount, this);
+ this.loanTermVariations.add(loanVariationTerms);
+ } else {
+ this.fixedEmiAmount = emiAmount;
+ }
isEmiAmountChanged = true;
}
@@ -2315,7 +2326,8 @@ public class Loan extends AbstractPersistable {
loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(),
getExpectedFirstRepaymentOnDate(), scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(),
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
- this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
+ this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
+ this.loanProduct.getInstalmentAmountInMultiplesOf());
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
scheduleGeneratorDTO.getHolidayDetailDTO());
@@ -4639,7 +4651,7 @@ public class Loan extends AbstractPersistable {
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails,
- calendarInstanceForInterestRecalculation, recalculationFrequencyType);
+ calendarInstanceForInterestRecalculation, recalculationFrequencyType, this.loanProduct.getInstalmentAmountInMultiplesOf());
return loanApplicationTerms;
}
@@ -4682,7 +4694,8 @@ public class Loan extends AbstractPersistable {
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(null, loanTermFrequency,
loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), null,
getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount,
- disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
+ disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
+ this.loanProduct.getInstalmentAmountInMultiplesOf());
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc, charges());
@@ -4880,10 +4893,12 @@ public class Loan extends AbstractPersistable {
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
loanVariationTermsData.add(data);
- return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
- dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
- inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData,
- maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate);
+ return LoanApplicationTerms
+ .assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType,
+ expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
+ inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount,
+ disbursementData, maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate,
+ this.loanProduct.getInstalmentAmountInMultiplesOf());
}
/**
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index 8b0a7b649..02673ec6b 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -117,7 +117,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
}
Money reducePrincipal = totalCumulativePrincipal.zero();
- Money fixedEmiAmount = totalCumulativePrincipal.zero();
int daysCalcForInstallmentNumber = 0;
LocalDate scheduleStartDateAsPerFrequency = periodStartDate;
while (!outstandingBalance.isZero()) {
@@ -191,7 +190,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
BigDecimal disburseAmt = BigDecimal.ZERO;
if (loanApplicationTerms.isMultiDisburseLoan()) {
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
- fixedEmiAmount = fixedEmiAmount.zero().plus(loanApplicationTerms.getFixedEmiAmount());
final Collection disbursementDatas = new ArrayList<>();
LocalDate tillDate = scheduledDueDate;
if (prepayDate != null && prepayDate.isBefore(tillDate)) {
@@ -233,16 +231,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
if (!loanApplicationTerms.isMultiDisburseLoan()) {
loanApplicationTerms.setFixedEmiAmount(null);
}
- fixedEmiAmount = fixedEmiAmount.zero();
outstandingBalance = outstandingBalance.minus(reducePrincipal);
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
reducePrincipal = reducePrincipal.zero();
break;
case REDUCE_NUMBER_OF_INSTALLMENTS:
- if (fixedEmiAmount.isGreaterThanZero()) {
- loanApplicationTerms.setFixedEmiAmount(fixedEmiAmount.getAmount());
- }
outstandingBalance = outstandingBalance.minus(reducePrincipal);
balanceForcalculation = balanceForcalculation.minus(reducePrincipal);
totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal);
@@ -385,14 +379,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
// Exclude principal portion for interest only installments
if (!recalculatedInterestComponent) {
principalForThisPeriod = principalInterestForThisPeriod.principal();
- if (diffAmt != null && !diffAmt.isEmpty() && loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
- Money principalToBeAdjust = principalForThisPeriod.zero();
- if (fixedEmiAmount.isZero()) {
- principalToBeAdjust = principalInterestForThisPeriod.interest().minus(interestForThisinstallment);
-
- } else {
- principalToBeAdjust = fixedEmiAmount.minus(principalForThisPeriod).minus(interestForThisinstallment);
- }
+ if (((diffAmt != null && !diffAmt.isEmpty()) || interestToBeAdded.compareTo(BigDecimal.ZERO) == 1)
+ && loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
+ Money principalToBeAdjust = Money.of(currency, loanApplicationTerms.getFixedEmiAmount()).minus(principalForThisPeriod)
+ .minus(interestForThisinstallment);
principalForThisPeriod = principalForThisPeriod.plus(principalToBeAdjust);
principalForThisPeriod = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisPeriod,
totalCumulativePrincipal.plus(principalForThisPeriod), periodNumber);
@@ -407,9 +397,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
}
- if (fixedEmiAmount.isZero() && !recalculatedInterestComponent) {
- fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
- }
reducePrincipal = reducePrincipal.minus(reducePrincipalForCurrentInstallment);
if (principalForThisPeriod.isGreaterThan(reducePrincipalForCurrentInstallment)) {
@@ -543,6 +530,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
}
}
+ loanApplicationTerms.resetFixedEmiAmount();
+
return LoanScheduleModel.from(periods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected,
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected,
totalOutstanding);
@@ -794,12 +783,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
Money principalDue = Money.of(currency, period.principalDue());
Money interestDue = Money.of(currency, period.interestDue());
-
- if(principalDue.isZero() && interestDue.isZero()) {
+
+ if (principalDue.isZero() && interestDue.isZero()) {
period.updateFeeChargesDue(Money.zero(currency));
period.updatePenaltyChargesDue(Money.zero(currency));
}
-
+
Money feeChargesDue = Money.of(currency, period.feeChargesDue());
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index 4aefa7775..f64378b7c 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -112,6 +112,8 @@ public final class LoanApplicationTerms {
private BigDecimal fixedEmiAmount;
+ private BigDecimal actualFixedEmiAmount;
+
private final BigDecimal maxOutstandingBalance;
private final List emiAmountVariations;
@@ -132,6 +134,8 @@ public final class LoanApplicationTerms {
private final RecalculationFrequencyType recalculationFrequencyType;
+ private final Integer instalmentAmountInMultiplesOf;
+
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType,
@@ -144,7 +148,8 @@ public final class LoanApplicationTerms {
final Money inArrearsTolerance, final boolean multiDisburseLoan, final BigDecimal emiAmount,
final List disbursementDatas, final BigDecimal maxOutstandingBalance,
final List emiAmountVariations, final Integer graceOnArrearsAgeing,
- final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled) {
+ final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled,
+ Integer instalmentAmountInMultiplesOf) {
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
@@ -157,7 +162,7 @@ public final class LoanApplicationTerms {
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
- recalculationFrequencyType);
+ recalculationFrequencyType, instalmentAmountInMultiplesOf);
}
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
@@ -166,7 +171,8 @@ public final class LoanApplicationTerms {
final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
final List disbursementDatas, final BigDecimal maxOutstandingBalance,
- final List emiAmountVariations, final LocalDate interestChargedFromDate) {
+ final List emiAmountVariations, final LocalDate interestChargedFromDate,
+ Integer instalmentAmountInMultiplesOf) {
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
@@ -201,7 +207,8 @@ public final class LoanApplicationTerms {
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance,
multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
- rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType);
+ rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType,
+ instalmentAmountInMultiplesOf);
}
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
@@ -211,7 +218,7 @@ public final class LoanApplicationTerms {
final BigDecimal emiAmount, final List disbursementDatas, final BigDecimal maxOutstandingBalance,
final List emiAmountVariations, final LocalDate interestChargedFromDate,
final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance,
- final RecalculationFrequencyType recalculationFrequencyType) {
+ final RecalculationFrequencyType recalculationFrequencyType, Integer instalmentAmountInMultiplesOf) {
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
@@ -248,7 +255,7 @@ public final class LoanApplicationTerms {
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(),
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
- interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType);
+ interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, instalmentAmountInMultiplesOf);
}
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
@@ -266,7 +273,7 @@ public final class LoanApplicationTerms {
applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType, applicationTerms.daysInYearType,
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
- applicationTerms.recalculationFrequencyType);
+ applicationTerms.recalculationFrequencyType, applicationTerms.instalmentAmountInMultiplesOf);
}
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
@@ -283,7 +290,8 @@ public final class LoanApplicationTerms {
final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
final boolean isInterestRecalculationEnabled, final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
- final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType) {
+ final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
+ final Integer instalmentAmountInMultiplesOf) {
this.currency = currency;
this.loanTermFrequency = loanTermFrequency;
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
@@ -313,6 +321,7 @@ public final class LoanApplicationTerms {
this.inArrearsTolerance = inArrearsTolerance;
this.multiDisburseLoan = multiDisburseLoan;
this.fixedEmiAmount = emiAmount;
+ this.actualFixedEmiAmount = emiAmount;
this.disbursementDatas = disbursementDatas;
this.maxOutstandingBalance = maxOutstandingBalance;
this.emiAmountVariations = emiAmountVariations;
@@ -324,6 +333,7 @@ public final class LoanApplicationTerms {
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
this.restCalendarInstance = restCalendarInstance;
this.recalculationFrequencyType = recalculationFrequencyType;
+ this.instalmentAmountInMultiplesOf = instalmentAmountInMultiplesOf;
}
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
@@ -508,15 +518,6 @@ public final class LoanApplicationTerms {
return periodNumber == numberOfRepayments;
}
- private Money calculateTotalDueForEqualInstallmentLoan(final BigDecimal periodicInterestRate) {
-
- final int periodsElapsed = 0;
- final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
- double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
-
- return Money.of(this.principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
- }
-
/**
* general method to calculate totalInterestDue discounting any grace
* settings
@@ -801,14 +802,21 @@ public final class LoanApplicationTerms {
private double paymentPerPeriod(final BigDecimal periodicInterestRate, final Money balance, final int periodsElapsed) {
- if (fixedEmiAmount != null) { return fixedEmiAmount.doubleValue(); }
- final double futureValue = 0;
- final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
+ if (this.fixedEmiAmount == null) {
+ final double futureValue = 0;
+ final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
- final Integer periodsRemaining = this.numberOfRepayments - periodsElapsed;
+ final Integer periodsRemaining = this.numberOfRepayments - periodsElapsed;
- return FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(), principalDouble, futureValue,
- false);
+ double installmentAmount = FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(),
+ principalDouble, futureValue, false);
+
+ if (this.instalmentAmountInMultiplesOf != null) {
+ installmentAmount = Money.roundToMultiplesOf(installmentAmount, this.instalmentAmountInMultiplesOf);
+ }
+ setFixedEmiAmount(BigDecimal.valueOf(installmentAmount));
+ }
+ return this.fixedEmiAmount.doubleValue();
}
private Money calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
@@ -1014,6 +1022,10 @@ public final class LoanApplicationTerms {
this.fixedEmiAmount = fixedEmiAmount;
}
+ public void resetFixedEmiAmount() {
+ this.fixedEmiAmount = this.actualFixedEmiAmount;
+ }
+
public LoanRescheduleStrategyMethod getLoanRescheduleStrategyMethod() {
return LoanRescheduleStrategyMethod.REDUCE_EMI_AMOUNT;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index d877706dc..d96d7a853 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -249,6 +249,8 @@ public class LoanScheduleAssembler {
final DaysInYearType daysInYearType = loanProduct.fetchDaysInYearType();
final boolean isInterestRecalculationEnabled = loanProduct.isInterestRecalculationEnabled();
+
+ final Integer instalmentAmountInMultiplesOf = loanProduct.getInstalmentAmountInMultiplesOf();
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod,
@@ -256,7 +258,7 @@ public class LoanScheduleAssembler {
principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney,
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData,
- graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled);
+ graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, instalmentAmountInMultiplesOf);
}
private List fetchDisbursementData(final JsonObject command) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
index f8942ddc1..dcda24532 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
@@ -95,19 +95,19 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
* TODO: Vishwas, this is probably not required, test and remove the
* same
**/
+ final Long productId = this.fromJsonHelper.extractLongNamed("productId", query.parsedJson());
+ final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
+ if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
+
if (validateParams) {
boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans();
- this.loanApiJsonDeserializer.validateForCreate(query.json(), isMeetingMandatoryForJLGLoans);
+ this.loanApiJsonDeserializer.validateForCreate(query.json(), isMeetingMandatoryForJLGLoans, loanProduct);
}
this.fromApiJsonDeserializer.validate(query.json());
final List dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
- final Long productId = this.fromJsonHelper.extractLongNamed("productId", query.parsedJson());
- final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
- if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
-
if (loanProduct.useBorrowerCycle()) {
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", query.parsedJson());
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", query.parsedJson());
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
index 9cf83f342..9093805a6 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
@@ -78,7 +79,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
this.apiJsonHelper = apiJsonHelper;
}
- public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans) {
+ public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans, final LoanProduct loanProduct) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken